summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf <ulf.hofemeier@intel.com>2012-10-02 08:58:25 -0700
committerUlf <ulf.hofemeier@intel.com>2012-10-02 08:58:25 -0700
commit3aa94bb5874991ba92ffe52b384f5fb3ddf53a1c (patch)
tree1c148feadf76d107051bf15da3b7dbf1409cdb28
downloadopenbox-3aa94bb5874991ba92ffe52b384f5fb3ddf53a1c.tar.gz
openbox-3aa94bb5874991ba92ffe52b384f5fb3ddf53a1c.tar.bz2
openbox-3aa94bb5874991ba92ffe52b384f5fb3ddf53a1c.zip
Initial import of openbox.submit/trunk/20121002.160201
-rw-r--r--AUTHORS24
-rw-r--r--CHANGELOG1204
-rw-r--r--COMPLIANCE78
-rw-r--r--COPYING340
-rw-r--r--HACKING33
-rw-r--r--Makefile.am604
-rw-r--r--README23
-rw-r--r--README.GIT61
-rw-r--r--README.NLS3
-rwxr-xr-xbootstrap18
-rw-r--r--configure.ac238
-rw-r--r--data/Makefile4
-rw-r--r--data/autostart/Makefile4
-rw-r--r--data/autostart/autostart.in17
-rwxr-xr-xdata/autostart/openbox-autostart.in34
-rwxr-xr-xdata/autostart/openbox-xdg-autostart198
-rw-r--r--data/environment10
-rw-r--r--data/gnome-session/Makefile4
-rw-r--r--data/gnome-session/openbox-gnome-fallback.session6
-rw-r--r--data/gnome-session/openbox-gnome.session9
-rw-r--r--data/gnome-wm-properties/Makefile4
-rw-r--r--data/gnome-wm-properties/openbox.desktop13
-rw-r--r--data/menu.xml394
-rw-r--r--data/menu.xsd215
-rw-r--r--data/openbox.desktop16
-rw-r--r--data/openbox.pngbin0 -> 1528 bytes
-rw-r--r--data/rc.xml730
-rw-r--r--data/rc.xsd551
-rw-r--r--data/xbm/bullet.xbm4
-rw-r--r--data/xbm/close.xbm4
-rw-r--r--data/xbm/desk.xbm4
-rw-r--r--data/xbm/desk_toggled.xbm4
-rw-r--r--data/xbm/iconify.xbm4
-rw-r--r--data/xbm/max.xbm4
-rw-r--r--data/xbm/max_toggled.xbm4
-rw-r--r--data/xbm/shade.xbm4
-rw-r--r--data/xbm/shade_toggled.xbm4
-rw-r--r--data/xsession/Makefile4
-rw-r--r--data/xsession/openbox-gnome-session.in66
-rw-r--r--data/xsession/openbox-gnome.desktop.in8
-rw-r--r--data/xsession/openbox-kde-session.in20
-rw-r--r--data/xsession/openbox-kde.desktop.in8
-rw-r--r--data/xsession/openbox-session.in22
-rw-r--r--data/xsession/openbox.desktop.in8
-rw-r--r--debian/changelog.in6
-rw-r--r--debian/compat1
-rw-r--r--debian/conffiles2
-rw-r--r--debian/control16
-rw-r--r--debian/copyright21
-rw-r--r--debian/menu6
-rw-r--r--debian/postinst42
-rw-r--r--debian/postrm43
-rwxr-xr-xdebian/rules107
-rw-r--r--doc/Makefile4
-rw-r--r--doc/doxygen/comments11
-rw-r--r--doc/doxygen/doxygen.conf.in192
-rw-r--r--doc/obxprop.1.in46
-rw-r--r--doc/obxprop.1.sgml124
-rw-r--r--doc/openbox-gnome-session.1.in22
-rw-r--r--doc/openbox-gnome-session.1.sgml76
-rw-r--r--doc/openbox-kde-session.1.in22
-rw-r--r--doc/openbox-kde-session.1.sgml76
-rw-r--r--doc/openbox-session.1.in42
-rw-r--r--doc/openbox-session.1.sgml93
-rw-r--r--doc/openbox.1.in95
-rw-r--r--doc/openbox.1.sgml204
-rw-r--r--doc/rc-mouse-focus.xml636
-rw-r--r--gettext.h91
-rwxr-xr-xinstall-sh276
-rw-r--r--m4/Makefile.am33
-rw-r--r--m4/openbox.m4122
-rw-r--r--m4/x11.m4443
-rwxr-xr-xmakedeb33
-rw-r--r--obrender/Makefile4
-rw-r--r--obrender/button.c117
-rw-r--r--obrender/button.h11
-rw-r--r--obrender/color.c366
-rw-r--r--obrender/color.h51
-rw-r--r--obrender/font.c382
-rw-r--r--obrender/font.h42
-rw-r--r--obrender/geom.h38
-rw-r--r--obrender/gradient.c836
-rw-r--r--obrender/gradient.h27
-rw-r--r--obrender/icon.h422
-rw-r--r--obrender/image.c868
-rw-r--r--obrender/image.h32
-rw-r--r--obrender/imagecache.c140
-rw-r--r--obrender/imagecache.h58
-rw-r--r--obrender/instance.c309
-rw-r--r--obrender/instance.h57
-rw-r--r--obrender/mask.c82
-rw-r--r--obrender/mask.h32
-rw-r--r--obrender/obrender-3.5.pc.in14
-rw-r--r--obrender/render.c575
-rw-r--r--obrender/render.h462
-rw-r--r--obrender/test.c115
-rw-r--r--obrender/theme.c2169
-rw-r--r--obrender/theme.h198
-rw-r--r--obrender/version.h.in15
-rw-r--r--obt/Makefile4
-rw-r--r--obt/bsearch.h61
-rw-r--r--obt/ddparse.c816
-rw-r--r--obt/ddparse.h57
-rw-r--r--obt/display.c165
-rw-r--r--obt/display.h70
-rw-r--r--obt/internal.h27
-rw-r--r--obt/keyboard.c445
-rw-r--r--obt/keyboard.h86
-rw-r--r--obt/link.c238
-rw-r--r--obt/link.h116
-rw-r--r--obt/obt-3.5.pc.in14
-rw-r--r--obt/paths.c368
-rw-r--r--obt/paths.h50
-rw-r--r--obt/prop.c584
-rw-r--r--obt/prop.h322
-rw-r--r--obt/signal.c290
-rw-r--r--obt/signal.h46
-rwxr-xr-xobt/tests/bstest.c58
-rwxr-xr-xobt/tests/ddtest.c61
-rw-r--r--obt/tests/ddtest.desktop15
-rwxr-xr-xobt/tests/linktest.c48
-rwxr-xr-xobt/tests/watchtest.c50
-rw-r--r--obt/util.h37
-rw-r--r--obt/version.h.in15
-rw-r--r--obt/xml.c436
-rw-r--r--obt/xml.h89
-rw-r--r--obt/xqueue.c411
-rw-r--r--obt/xqueue.h101
-rw-r--r--openbox/Makefile4
-rw-r--r--openbox/actions.c463
-rw-r--r--openbox/actions.h119
-rw-r--r--openbox/actions/Makefile4
-rw-r--r--openbox/actions/addremovedesktop.c119
-rw-r--r--openbox/actions/all.c44
-rw-r--r--openbox/actions/all.h46
-rw-r--r--openbox/actions/breakchroot.c20
-rw-r--r--openbox/actions/close.c19
-rw-r--r--openbox/actions/cyclewindows.c249
-rw-r--r--openbox/actions/debug.c44
-rw-r--r--openbox/actions/decorations.c46
-rw-r--r--openbox/actions/desktop.c567
-rw-r--r--openbox/actions/directionalwindows.c468
-rw-r--r--openbox/actions/dock.c38
-rw-r--r--openbox/actions/dockautohide.c21
-rw-r--r--openbox/actions/execute.c273
-rw-r--r--openbox/actions/exit.c88
-rw-r--r--openbox/actions/focus.c74
-rw-r--r--openbox/actions/focustobottom.c17
-rw-r--r--openbox/actions/fullscreen.c20
-rw-r--r--openbox/actions/growtoedge.c200
-rw-r--r--openbox/actions/iconify.c23
-rw-r--r--openbox/actions/if.c213
-rw-r--r--openbox/actions/kill.c20
-rw-r--r--openbox/actions/layer.c132
-rw-r--r--openbox/actions/lower.c24
-rw-r--r--openbox/actions/maximize.c140
-rw-r--r--openbox/actions/move.c28
-rw-r--r--openbox/actions/moverelative.c84
-rw-r--r--openbox/actions/moveresizeto.c189
-rw-r--r--openbox/actions/movetoedge.c111
-rw-r--r--openbox/actions/omnipresent.c23
-rw-r--r--openbox/actions/raise.c22
-rw-r--r--openbox/actions/raiselower.c21
-rw-r--r--openbox/actions/reconfigure.c17
-rw-r--r--openbox/actions/resize.c198
-rw-r--r--openbox/actions/resizerelative.c104
-rw-r--r--openbox/actions/restart.c50
-rw-r--r--openbox/actions/shade.c46
-rw-r--r--openbox/actions/shadelowerraise.c40
-rw-r--r--openbox/actions/showdesktop.c17
-rw-r--r--openbox/actions/showmenu.c47
-rw-r--r--openbox/actions/unfocus.c17
-rw-r--r--openbox/client.c4603
-rw-r--r--openbox/client.h758
-rw-r--r--openbox/client_list_combined_menu.c167
-rw-r--r--openbox/client_list_combined_menu.h26
-rw-r--r--openbox/client_list_menu.c224
-rw-r--r--openbox/client_list_menu.h26
-rw-r--r--openbox/client_menu.c421
-rw-r--r--openbox/client_menu.h24
-rw-r--r--openbox/config.c1142
-rw-r--r--openbox/config.h225
-rw-r--r--openbox/debug.c201
-rw-r--r--openbox/debug.h43
-rw-r--r--openbox/dock.c693
-rw-r--r--openbox/dock.h88
-rw-r--r--openbox/event.c2287
-rw-r--r--openbox/event.h91
-rw-r--r--openbox/focus.c389
-rw-r--r--openbox/focus.h81
-rw-r--r--openbox/focus_cycle.c364
-rw-r--r--openbox/focus_cycle.h58
-rw-r--r--openbox/focus_cycle_indicator.c287
-rw-r--r--openbox/focus_cycle_indicator.h31
-rw-r--r--openbox/focus_cycle_popup.c847
-rw-r--r--openbox/focus_cycle_popup.h53
-rw-r--r--openbox/frame.c1866
-rw-r--r--openbox/frame.h272
-rw-r--r--openbox/framerender.c412
-rw-r--r--openbox/framerender.h26
-rw-r--r--openbox/geom.h166
-rw-r--r--openbox/grab.c244
-rw-r--r--openbox/grab.h58
-rw-r--r--openbox/group.c66
-rw-r--r--openbox/group.h44
-rw-r--r--openbox/keyboard.c340
-rw-r--r--openbox/keyboard.h48
-rw-r--r--openbox/keytree.c152
-rw-r--r--openbox/keytree.h45
-rw-r--r--openbox/menu.c729
-rw-r--r--openbox/menu.h227
-rw-r--r--openbox/menuframe.c1387
-rw-r--r--openbox/menuframe.h147
-rw-r--r--openbox/misc.h101
-rw-r--r--openbox/mouse.c413
-rw-r--r--openbox/mouse.h48
-rw-r--r--openbox/moveresize.c1092
-rw-r--r--openbox/moveresize.h52
-rw-r--r--openbox/mwm.h78
-rw-r--r--openbox/openbox.c779
-rw-r--r--openbox/openbox.h66
-rw-r--r--openbox/ping.c165
-rw-r--r--openbox/ping.h43
-rw-r--r--openbox/place.c593
-rw-r--r--openbox/place.h45
-rw-r--r--openbox/popup.c569
-rw-r--r--openbox/popup.h147
-rw-r--r--openbox/prompt.c603
-rw-r--r--openbox/prompt.h125
-rw-r--r--openbox/resist.c416
-rw-r--r--openbox/resist.h41
-rw-r--r--openbox/screen.c1761
-rw-r--r--openbox/screen.h175
-rw-r--r--openbox/session.c866
-rw-r--r--openbox/session.h60
-rw-r--r--openbox/stacking.c710
-rw-r--r--openbox/stacking.h85
-rw-r--r--openbox/startupnotify.c276
-rw-r--r--openbox/startupnotify.h50
-rw-r--r--openbox/translate.c160
-rw-r--r--openbox/translate.h29
-rw-r--r--openbox/window.c229
-rw-r--r--openbox/window.h93
-rw-r--r--packaging/openbox.changes7
-rw-r--r--packaging/openbox.spec145
-rw-r--r--po/LINGUAS32
-rw-r--r--po/Makevars41
-rw-r--r--po/POTFILES.in18
-rw-r--r--po/ar.po476
-rw-r--r--po/bn_IN.po480
-rw-r--r--po/ca.po523
-rw-r--r--po/cs.po501
-rw-r--r--po/da.po505
-rw-r--r--po/de.po517
-rw-r--r--po/en@boldquot.po499
-rw-r--r--po/en@quot.po496
-rw-r--r--po/es.po523
-rw-r--r--po/et.po500
-rw-r--r--po/eu.po506
-rw-r--r--po/fi.po499
-rw-r--r--po/fr.po527
-rw-r--r--po/hr.po503
-rw-r--r--po/hu.po496
-rw-r--r--po/it.po520
-rw-r--r--po/ja.po502
-rw-r--r--po/lt.po496
-rw-r--r--po/lv.po503
-rw-r--r--po/nl.po504
-rw-r--r--po/no.po506
-rw-r--r--po/openbox.pot453
-rw-r--r--po/pl.po496
-rw-r--r--po/pt.po503
-rw-r--r--po/pt_BR.po517
-rw-r--r--po/ru.po501
-rw-r--r--po/sk.po487
-rw-r--r--po/sr.po508
-rw-r--r--po/sr@latin.po511
-rw-r--r--po/sv.po503
-rw-r--r--po/tr.po503
-rw-r--r--po/uk.po500
-rw-r--r--po/vi.po503
-rw-r--r--po/zh_CN.po495
-rw-r--r--po/zh_TW.po495
-rw-r--r--tests/Makefile6
-rw-r--r--tests/aspect.c79
-rw-r--r--tests/big.c68
-rw-r--r--tests/borderchange.c107
-rw-r--r--tests/confignotify.c89
-rw-r--r--tests/confignotifymax.c97
-rw-r--r--tests/cursorio.c57
-rw-r--r--tests/duplicatesession.c66
-rw-r--r--tests/extentsrequest.c133
-rw-r--r--tests/fakeunmap.c60
-rw-r--r--tests/fallback.c64
-rw-r--r--tests/focusout.c224
-rw-r--r--tests/fullscreen.c102
-rw-r--r--tests/grav.c80
-rw-r--r--tests/groupmodal.c80
-rw-r--r--tests/grouptran.c72
-rw-r--r--tests/grouptran2.c79
-rw-r--r--tests/grouptrancircular.c73
-rw-r--r--tests/grouptrancircular2.c79
-rwxr-xr-xtests/hideshow.py78
-rw-r--r--tests/iconifydelay.c67
-rw-r--r--tests/icons.c251
-rw-r--r--tests/mapiconic.c80
-rw-r--r--tests/mingrow.c100
-rw-r--r--tests/modal.c64
-rw-r--r--tests/modal2.c74
-rw-r--r--tests/modal3.c78
-rw-r--r--tests/noresize.c82
-rw-r--r--tests/oldfullscreen.c94
-rw-r--r--tests/override.c76
-rw-r--r--tests/overrideinputonly.c58
-rw-r--r--tests/positioned.c73
-rw-r--r--tests/resize.c69
-rw-r--r--tests/restack.c139
-rw-r--r--tests/shape.c77
-rw-r--r--tests/showhide.c54
-rw-r--r--tests/skiptaskbar.c63
-rw-r--r--tests/skiptaskbar2.c68
-rw-r--r--tests/stackabove.c73
-rw-r--r--tests/stacking.c80
-rw-r--r--tests/strut.c84
-rw-r--r--tests/title.c86
-rw-r--r--tests/urgent.c77
-rw-r--r--tests/usertimewin.c74
-rw-r--r--tests/wmhints.c97
-rw-r--r--themes/Artwiz-boxed/openbox-3/themerc102
-rw-r--r--themes/Bear2/openbox-3/close.xbm4
-rw-r--r--themes/Bear2/openbox-3/close_pressed.xbm5
-rw-r--r--themes/Bear2/openbox-3/desk.xbm4
-rw-r--r--themes/Bear2/openbox-3/desk_toggled.xbm4
-rw-r--r--themes/Bear2/openbox-3/iconify.xbm4
-rw-r--r--themes/Bear2/openbox-3/iconify_pressed.xbm5
-rw-r--r--themes/Bear2/openbox-3/max.xbm4
-rw-r--r--themes/Bear2/openbox-3/max_pressed.xbm5
-rw-r--r--themes/Bear2/openbox-3/max_toggled.xbm4
-rw-r--r--themes/Bear2/openbox-3/shade.xbm4
-rw-r--r--themes/Bear2/openbox-3/shade_pressed.xbm5
-rw-r--r--themes/Bear2/openbox-3/themerc83
-rw-r--r--themes/Clearlooks-3.4/openbox-3/themerc126
-rw-r--r--themes/Clearlooks-Olive/openbox-3/themerc121
-rw-r--r--themes/Clearlooks/openbox-3/themerc164
-rw-r--r--themes/Makefile4
-rw-r--r--themes/Mikachu/openbox-3/bullet.xbm4
-rw-r--r--themes/Mikachu/openbox-3/close.xbm5
-rw-r--r--themes/Mikachu/openbox-3/desk.xbm5
-rw-r--r--themes/Mikachu/openbox-3/iconify.xbm5
-rw-r--r--themes/Mikachu/openbox-3/max.xbm5
-rw-r--r--themes/Mikachu/openbox-3/themerc174
-rw-r--r--themes/Natura/openbox-3/close.xbm4
-rw-r--r--themes/Natura/openbox-3/close_hover.xbm5
-rw-r--r--themes/Natura/openbox-3/desk.xbm4
-rw-r--r--themes/Natura/openbox-3/desk_hover.xbm4
-rw-r--r--themes/Natura/openbox-3/desk_toggled.xbm4
-rw-r--r--themes/Natura/openbox-3/iconify.xbm4
-rw-r--r--themes/Natura/openbox-3/iconify_hover.xbm4
-rw-r--r--themes/Natura/openbox-3/max.xbm4
-rw-r--r--themes/Natura/openbox-3/max_hover.xbm5
-rw-r--r--themes/Natura/openbox-3/max_toggled.xbm4
-rw-r--r--themes/Natura/openbox-3/shade.xbm4
-rw-r--r--themes/Natura/openbox-3/shade_hover.xbm4
-rw-r--r--themes/Natura/openbox-3/themerc99
-rw-r--r--themes/Onyx-Citrus/openbox-3/themerc85
-rw-r--r--themes/Onyx/openbox-3/themerc85
-rw-r--r--themes/Orang/openbox-3/themerc100
-rw-r--r--themes/Syscrash/openbox-3/max.xbm4
-rw-r--r--themes/Syscrash/openbox-3/max_disabled.xbm4
-rw-r--r--themes/Syscrash/openbox-3/max_pressed.xbm4
-rw-r--r--themes/Syscrash/openbox-3/max_toggled.xbm4
-rw-r--r--themes/Syscrash/openbox-3/themerc114
-rw-r--r--tools/gdm-control/Makefile4
-rw-r--r--tools/gdm-control/gdm-control.c295
-rw-r--r--tools/gnome-panel-control/Makefile4
-rw-r--r--tools/gnome-panel-control/gnome-panel-control.c100
-rw-r--r--tools/obxprop/Makefile4
-rw-r--r--tools/obxprop/obxprop.c351
-rwxr-xr-xtools/themeupdate/themeupdate.py410
-rw-r--r--version.h.in6
380 files changed, 78331 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..b00cb38
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,24 @@
+Openbox authors/contributors:
+
+Mikael Magnusson (mikachu@comhem.se)
+ - Developer
+Dana Jansens (danakj@orodu.net)
+ - Lead developer
+Derek Foreman (manmower@openbox.org)
+ - Rendering code
+Tore Anderson (tore@linpro.no)
+ - Directional focus, edge moving/growing actions
+Audun Hove (audun@nlc.no)
+ - Actions code, move window to edge actions
+Marius Nita (marius@cs.pdx.edu)
+ - Otk prototype code, design ideas.
+John McKnight (jmcknight@gmail.com)
+ - Clearlooks themes
+David Barr (david@chalkskeletons.com)
+ - Bear2 theme
+ - Clearlooks theme
+ - Icon
+Brandon Cash
+ - SplitVertical gradient style
+Dan Rise
+ - Natura theme
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..9bd1021
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,1204 @@
+3.5.0:
+ * New alt-tab dialog shows windows in a vertical list.
+ * Improved Xinerama support.
+ * Allow icons in menus.
+ * Theme options for prompt dialogs (osd.button.unpressed.*,
+ osd.button.pressed.*, osd.button.focused.*)
+ * Addresses bug #4877, #4596, #4617, #4752, #4663, #4662, #4586, #2319,
+ #4341, #4519, #4543, #4503, #4355, #4072, #3702, #4284
+ * Lots of additional bug fixes and performance improvements.
+
+3.4.11.2:
+ * Updated Estonian and Portuguese translations.
+ * Fix a rare crash involving moving fullscreen windows to different monitors
+ * Fix a more common crash involving pressing right in a menu
+
+3.4.11.1:
+ * Updated Polish translation.
+ * Fixed bug #4519 (Incorrect focus in reused windows).
+ * Lower the default submenu open/hide delay from 200ms to 100ms.
+ * Fix some more problems with gnome integration.
+ * Update Clearlooks theme.
+ * Some other small fixes.
+
+3.4.11:
+ * Update Hungarian, Japanese, and Latvian translations.
+ * Make xdg-autostart use the OPENBOX environment by default, so you can use
+ OnlyShowIn=OPENBOX in an autostart .desktop and it will work as expected.
+ * Don't close the menu when you hold control and execute something.
+ * Fix bug #4503 (Adjust who shows up in the Alt-Tab list using SKIP_TASKBAR).
+ * Fix flickering window when moving maximized window between monitors of
+ different sizes.
+ * Fix bug #4355 (Allow multiple escaped _'s in a menu label and allow
+ a menu shortcut to come after an escaped _).
+ * Remember the maximized state of a window when it goes fullscreen, and
+ restore it when leaving fullscreen state.
+ * Fix bug #4072 (Openbox is stopped by terminal applications writing to
+ stdout).
+ * Fix bug #4492 (Mistake in openbox-gnome-session check while setting up).
+ * Fix obxprop to make --root and --id work correctly.
+ * Add _OB_APP_ROLE/CLASS/NAME/TYPE properties (replaces _OB_ROLE/CLASS/NAME).
+ * Make the focus cycling popup dynamic when windows appear/disappear.
+ * Fix bug #4411 (Crash when window appears during focus cycling).
+ * Allow the user to specify which properties should be shown by obxprop.
+ * Fix tilde expansion in the Execute action
+ * Make Home and End keys move to the top/bottom of the active menu.
+ * Use the submenuShowDelay when navigating menus with the keyboard.
+
+3.4.10:
+ * Improve keyboard navigation in Openbox menus.
+ * Add a --root option and a manpage for obxprop.
+ * Use a negative value for submenuShowDelay and submenuHideDelay to cause
+ an infinite delay. This means you have to click to show a submenu, rather
+ than just hover over it.
+ * Improved code for submenu show/hide delay. Added the submenuHideDelay
+ config file option, under the "menu" section.
+ * Fixed bug #4464 (Typo in openbox-gnome-session script).
+ * Fixed bug #4436 (Focusing a window used to stop focus cycling).
+ * Renamed obprop to obxprop due to collision with Open Babel (See bug #4419).
+
+3.4.9:
+ * Allow focus to move while inside an Openbox menu, or during an interactive
+ action such as window cycling.
+ * Fixed bug #3717 (Empty dock interfered with move/grow to edge actions).
+ * Fixed bug #4411 (Crash when switching desktops and window cycling).
+ * Fixed bug #4377 (Window resistance against struts).
+ * Fixed bug #4035 (Prevent focus from moving under the mouse after
+ activating a window with an Openbox menu.
+ * Correct the value provided by the _NET_WORKAREA hint, so desktop icons
+ will place across all monitors.
+ * Don't hide submenus immediately when moving through the parent menu.
+ (Resolves request #3762).
+ * Fix for showing Openbox menus with multiple monitors, don't restrict them
+ to the monitor where the mouse is.
+ * Fixed bug #4023 (Allow the user to have multiple keys which perform the
+ same function in Openbox menus/move/resize. E.g. two keys which are
+ both bound to Escape.
+ * Add a new obprop tool, which can be used to read the _OB_ROLE, _OB_NAME,
+ and _OB_CLASS (as well as any other UTF-8 window properties) off of a
+ window.
+ * Add _OB_ROLE, _OB_NAME, and _OB_CLASS hints on each window that show the
+ respective values for use in the rc.xml applications section, to modify
+ the window when it appears.
+ * Improve Openbox interoperability with gnome-session >= 2.24.
+ * Fixed bug #4040 (Remove desktop hints set by gdm in the openbox-session
+ scripts, so that Openbox can set the number of desktops (assuming
+ gnome-settings-daemon doesn't first)).
+ * Fix a bug in xdg-autostart preventing some .desktop files from working.
+ * Show the desktop pager popup on the primary monitor instead of on all
+ monitors.
+ * Add a new primaryMonitor config option, which is where Openbox popups
+ will appear. Defaults to a fixed monitor, but can be made to behave as
+ before with the "active" value for it.
+ * Correct edge detection for move/grow to edge to properly use monitor edges
+ for multi-monitor setups.
+ * Change default window placement policy to stay on the active monitor for
+ multi-monitor setups.
+ * Fixed bug #1149 (Crash with some window icon sizes).
+ * Respond to all strut changes, fixes moving/hiding panels.
+ * Fix internal code to focus windows on other desktops correctly (Fixes
+ bug #4413).
+ * Focus correctness fixups for switching desktops.
+ * Fixed bug #4373 (Decoration bug for shaded maximized windows).
+ * Fixed bug #4350 (Allow a window to be made skip_taskbar but still get
+ focused by the user's rc.xml).
+ * Fixed bug #4307 (Set a minimum time for screenEdgeWarpTime).
+ * Fixed bug #4253 (Support for Solaris in openbox-session scripts).
+ * Fixed bug #3851 (Allow transient windows to be above helper windows).
+
+3.4.8:
+ * Updated translations: Slovak.
+ * Allow windows to change their decorations at any time (Fix for
+ Google Chrome).
+ * Make openbox-session to respect the $XDG_CONFIG_HOME environment variable.
+ * Fixed bug #4344 where borders were given to windows that should not have
+ them.
+ * Merge the SessionLogout and Exit actions. They now test if connected to a
+ session manager and ask it to exit if so, or simply kill Openbox if not.
+ * Further tweaks to the _NET_ACTIVE_WINDOW message handling. Use the same
+ logic for focus-stealing as is used when mapping a new window.
+ * Don't go out of our way any more to prevent focus from moving while the
+ keyboard is grabbed.
+ * Fix openbox-gnome-session when using gnome-session > 2.22.
+
+3.4.8-rc2:
+ * Updated translations: Italian, Croatian, Ukrainian.
+ * When resizing a window while focus cycling with bar=no, the bar no longer
+ reappears.
+ * Correctly handle shaped windows using the ShapeInput kind, this is used
+ by many composited apps to pass through clicks in their transparent areas.
+ * Fix the <monitor> per-app setting.
+ * Avoid using anonymous unions.
+ * Windows that had their decorations removed by per-app settings were still
+ placed as if they still had their decorations.
+ * Fix event handling not to ignore events on a window when they have an unmap
+ event in the queue, if that unmap event doesn't cause the window to be
+ unmanaged.
+ * Show the desktop switch on every monitor in xinerama.
+ * Fix interpretation of struts in xinerama where the screens have different
+ sizes.
+ * Add "next" and "prev" as possible <monitor> targets in the moveto and
+ resizeto actions.
+ * Allow escaping the _ used to mark the shortcut character in menu labels.
+ You can now change the (first) _ in a label to __, this will be displayed
+ as a single underscore. The rest of the _ in the string will be unaffected,
+ so only double the first one.
+ * Only replace ~ with the home directory when it is preceded by whitespace or
+ is at the start of the string, and when it is followed by a space, slash, or
+ the end of the string. This is implemented with GRegex, and so the required
+ glib version has been bumped to 2.14.
+ * Some other small fixes.
+
+3.4.8-rc1:
+ * Updated translations: Basque, Catalan, Turkish, Italian, Spanish, Russian.
+ * New translations: Danish, Turkish, Lithuanian.
+ * Set the _MOTIF_WM_INFO atom so urxvt uses motif hints for borderless mode.
+ * Properly escape the xml used in session files.
+ * Correct a 64-bit issue related to comparing timestamps.
+ * There is a sneaky sentence right at the end of a big paragraph in the
+ wm-spec document that says windows mapping with _NET_WM_USER_TIME=0 should
+ not be focused initially, honor this request.
+ * When moving a window to another desktop with following on, bring the
+ window's helper windows (for example gimp image windows with the toolbox
+ set to utility window).
+ * Change the _NET_ACTIVE_WINDOW messages again, if they originate from the
+ app and the window is on another desktop, just set the demands_attention
+ flag. If the event came from the user (ie pager/panel), then the window
+ is still moved to the current desktop.
+
+3.4.7.2:
+ * The system I used to generate the dist tarball didn't have the
+ docbook-to-man command so the manpages were empty.
+
+3.4.7.1:
+ * Not to be outdone by the cairo team, I introduced a bug in the last release
+ which made resizing not give any feedback. This is now fixed.
+
+3.4.7:
+ * Fully updated Czech, Simplified Chinese, Traditional Chinese, German,
+ French, Hungarian, Norwegian, Vietnamese, Dutch, Swedish, Finnish,
+ Brazilian Portuguese, Japanese and Portuguese translations
+ * Partially updated Spanish translation
+ * Add an example of the "force" option for the per-app placement options to
+ the default rc.xml file
+ * Add a new xdg-autostart script. This will eventually end up in the PyXDG
+ distribution hopefully, but it is included in Openbox for now. This script
+ runs things based on the freedesktop.org autostart specification. You can
+ have it run GNOME, KDE, ROX, or XFCE specific things if you want. The
+ new default system-wide autostart.sh script runs it automatically
+ * Update the default menu.xml to include a lot of common apps
+ * Fix white font shadows (negative shadowtint)
+ * Update the autostart.sh to find gnome-settings-daemon correctly, as the
+ GNOME people have moved it to libexec
+ * Fix focus possibly getting stolen when using the Focus action
+ * Drastically speed up rendering of Vertical and SplitVertical gradients
+ * Speed improvements also for Horizonal and Pyramid gradients
+ * Add new theme options, menu.overlap.x and menu.overlap.y options, that let
+ you independently control the horizontal and vertical position of submenus
+ * Change _NET_ACTIVE_WINDOW messages to not change the current desktop, but
+ to bring the window to the current desktop instead. This is the industry
+ standard policy
+ * Use the pretty new openbox.png icon as the default window icon
+ * Allow matching per-application rules to windows by their window type
+ (normal, dialog, splash, etc). The default rc.xml has more details
+ * Add new Openbox-themed prompt windows. Use these prompt windows to ask
+ before killing off windows that aren't responding. This also means we
+ don't need to ping every window constantly forever
+ * Add a new <prompt> option to the Execute action. If this is set to a
+ string, a dialog will be shown with that string in it and "yes"/"no"
+ buttons. The command to be executed will only be run if the user selects
+ "yes" in the dialog
+ * Add a new <prompt> option to the Exit action, which is a boolean (not a
+ string). When true, Openbox will show a dialog confirming if you want to
+ exit. The default is to show the prompt
+ * Reduce Openbox's memory footprint and speed up rendering through the use
+ of a new icon cache, so that Openbox only needs to keep 1 copy of an icon
+ when 100 different windows share it
+ * Make Openbox menus have the "menu type" hint for compositors to see and use
+ * Fix the MoveResizeTo action for negative coords (opposite edges)
+ * Fix key bindings getting lost if multiple bindings at the same level could
+ not be translated (Fixes VMWare causing Openbox keybindings to stop
+ working)
+ * Fix the resize popup box for terminal windows with a base size of 0 (show
+ the right size values for urxvt terminals)
+ * Fix some off-by-one bugs with the edge growing/shrinking code
+ * Add new theme options for menu line separators: menu.separator.color,
+ menu.separator.width, menu.separator.padding.width,
+ menu.separator.padding.height
+ * Add xfce-mcs-manager to the default autostart.sh, and run it automatically
+ when gnome-settings-daemon is not present to have GTK apps inherit settings
+ from the XFCE configuration tools
+ * Make the send-to-desktop menu in the client-menu indicate which is the current
+ desktop for omnipresent windows, and don't close it if just toggling
+ omni-presence when ctrl-clicking
+ * Add a new SessionLogout action that logs out through the session manager,
+ when running Openbox within a session manager such as within an
+ GNOME/Openbox or KDE/Openbox session. The action includes a <prompt>
+ option which is similar to the Exit action's
+ * Add a new gdm-control command that lets you control gdm from within an X
+ session. The gdm-control lets you change GDM's behaviour for when you end
+ the current session. For instance, you can tell GDM to reboot, and
+ then immediately log out of the current session, and the computer will be
+ rebooted
+ * Show an information dialog when an error occurs for Openbox, such as when
+ the Execute action fails or when XML syntax errors are present in the
+ configuration files
+ * When making a window very narrow, don't draw buttons to the right of the title
+ on top of the ones on its left.
+
+3.4.6.1:
+ * Updated Clearlooks theme
+ * Add the force="yes/no" option for the per-app settings' <position> tag
+ * Raise and focus modal children and their direct parents together, improved
+ usability with direct modal transient windows
+ * Fix crash when using <raise> for NextWindow and there are no windows
+ to move focus to
+ * Add the <manageDesktops> option in the rc.xml <menu> section, which toggles
+ the "Manage Desktops" section appearing in the combined-client-list-menu
+ * Fix for menu headers showing the wrong text
+ * Fix for the <focusLast> behavior
+ * Treat modal direct children as one window with their parent consistently
+
+3.4.6:
+ * Added Basque translation
+ * Updated French, Vietnamese German, Simplified Chinese, Russian, Portuguese,
+ Brasilian Portuguese, Norwegian, and Finnish translations
+ * New Clearlooks theme, updated by David Barr
+ * Updated the previous Clearlooks theme, and renamed it to Clearlooks-3.4
+ * Allow dialog type windows to go fullscreen (Fixes Kpdf)
+ * Remove the extraneous top border for undecorated windows while maximized
+ * Fixes for keyboard modifiers (Alt-tab dialog getting stuck on screen for
+ some users)
+ * Automatically catch changes to the keyboard map and reconfigure the key
+ bindings on the fly
+ * Fix focus moving incorrectly sometimes with focus under mouse enabled
+ * Make default configuration focus the desktop when you right click
+ * Add the <bar> and <raise> options for all window cycling actions, allowing
+ you to have your target window temporarily raised above all others, and to
+ turn the focus target indicator bar off
+ * Improve the LastDesktop action to not remember desktops you skipped across
+ * Ignore mouse clicks that are made on override-redirect (unmanaged) windows
+ * When opening a menu with a key binding, don't use the key binding to run
+ something in the menu
+ * Add a <monitor> option for window placement, which gives you the option
+ to place new windows on the active monitor, or the monitor where the mouse
+ is, instead of on any monitor (for xinerama multihead setups)
+ * Add options for placing the window move/resize coordinate popup window in
+ a fixed position on screen, rather than relative to the window being
+ moved or resized
+ * Prevent the dock from auto-hiding completely offscreen if the theme has
+ no borders for it
+ * New icon
+ * Fix race condition when running things that want to grab the keyboard
+ (e.g. gnome-panel-control --main-menu)
+ * When dialog windows ask to not appear in the taskbar, still give them focus
+ in normal ways (fixes new GNOME session logout dialogs)
+ * Fix bug with resizing corners on certain parts of the window frame
+ * Ping applications to tell when they are running or have become frozen.
+ Show a [Not Responding] message in the title bar of windows which are
+ frozen.
+ * When closing a window which is [Not Responding], kill the window's process
+ if it is running on the same machine as Openbox. Otherwise, just
+ disconnect
+ the window from the X display. A second attempt to close a [Not
+ Responding]
+ window will kill it forcefully (kill -9).
+ * Fixes for internal timers
+ * Add a <wmclass> option for the execute action's startup-notification. This
+ lets you tell Openbox that the application will map a window with the
+ specified class - for applications that do not support startup-notification
+ natively.
+ * Fix for empty dock taking up space onscreen after a reconfigure
+ * Reduce Openbox's additional memory footprint per-window and per-menu
+ * Faster horizontal gradient rendering
+ * Don't deiconify windows that aren't allowed to be directly iconified on
+ restart (eg toolbars), as they can be iconified by other means
+ * Improve support for fullscreen windows in xinerama (TwinView) and
+ multiple-screen setups
+ * Add a --config-file command line option, to specify an alternate
+ configuration file path
+
+3.4.5:
+ * Added Hungarian translation
+ * Updated Finnish, Russian, German and French translations
+ * Fixed some very minor memory leaks
+ * Hide the desktop popup when showing the focus popup
+ * Fix a crash when trying to access the More... menu of
+ client-list-combined-menu
+ * Fix the coordinate popup only showing up on the first monitor in xinerama
+ * Add --exit to exit the currently running openbox instance
+
+3.4.4:
+ * Updated Traditional Chinese translation
+ * Updated Norwegian translation
+ * Fix for MoveToEdge skipping edges sometimes
+ * Let you specify the vertical and horizontal padding independently in themes
+ * Fix so that if you select an omnipresent window on another desktop in the
+ client list menus, it will go to that desktop
+ * Make the GrowToEdge action shrink once there is no more room to grow,
+ similar to in 3.4.2, but shrinking to edges as well
+ * Move the Send To and Layers submenus to the top of the client menu
+ * Fix race causing omnipresent windows to lose focus when switching desktops
+ very quickly
+ * Don't focus new windows on other desktops if they aren't related to the
+ currently focused window
+ * Add corner resizing areas in the inner client border (Fixes themes such
+ as Onyx)
+ * New focus stealing prevention that is smart and not intrusive and not
+ annoying
+ * Revert a small change in 3.4.3 that caused windows to be placed funny in
+ Smart placement when there was a dock or something on the side of the
+ screen
+ * Show a notification when switching desktops
+ * Fix for delayed focus-follows-mouse interrupting move/resize or menus
+ * Make screen edge warp keep warping without having the move the mouse 1
+ pixel
+ * Fix for resizing terminals in the top/left sides getting a little confused
+ * Fix to keep oldschool (Non-EWMH) fullscreen windows from being moved and
+ resized inside of the struts (Fixes Acroread)
+ * Accept the <command> option for the Restart command, similar to the Execute
+ action
+ * Don't make clicking on dock apps sometimes act like clicking on the root
+ window (Don't propogate button events up)
+ * Fix a bug introduced in 3.4.3 which caused the Windows key as a modifier
+ for bindings to not work properly
+ * Let windows skip across monitors in a Xinerama setup when using MoveToEdge
+ or Shift-arrow in an interactive move
+ * Make move and resize take the dock into account for resistance
+ * Raise new windows when it makes sense, when they aren't being focused
+ * Change default config to use click events for mouse wheel bindings instead
+ of presses
+
+3.4.3:
+ * Add Ukrainian translation
+ * Updated translations:
+ - Japanese, Dutch, Polish, Italian, Estonian, German, Portuguese,
+ Vietnamese, Finnish, Czech, Arabic, Spanish, Swedish, French
+ * Improve focus fallback - don't fallback to windows you can't focus cycle
+ (Alt-Tab) to
+ * Don't show the client border for undecorated windows when keepBorder is on,
+ just the outer border
+ * Some improvements in the smart placement to help it find a place more
+ often. This needs more work still.
+ * Don't focus windows that appear under the mouse in mouse-focus mode unless
+ underMouse is enabled
+ * Don't move focus when the window under the pointer iconifies unless
+ underMouse is enabled
+ * Respect min/max window sizes when a window is fullscreened or maximized.
+ This fixes problems experienced with maximizing vnc windows
+ * Don't XUngrabKeyboard unless we need to
+ * Correct calculations for the menu header width, so that they are not
+ ellisized unnecessarily
+ * Advanced support for partial struts. When you maximize a window it will
+ now only use the struts it needs to. This can greatly improve
+ xinerama functionality.
+ * Fixes for how the dock is placed in certain positions on screen (off by 1
+ errors)
+ * New default bindings:
+ - don't unshade when clicking on the titlebar
+ - raise when unshading with the scrollwheel
+ - lower when shading with the scrollwheel
+ * Don't show the top resize area in the titlebar when a window is shaded
+ * Optimize rendering of window decorations
+ * Optimize splitvertical gradients - no more malloc/free during render
+ * Let the theme specify all colors for a splitvertical gradient with the new
+ .splitTo theme elements
+ * Improve decorations for maximized windows. Don't draw the side borders in
+ the titlebar.
+ * Don't resist against desklet windows (below layer + skip taskbar)
+ * New actions for dynamically adjusting your desktop workspaces:
+ - AddDesktopLast, RemoveDesktopLast
+ - AddDesktopCurrent, RemoveDestopCurrent
+ * New <center> option for smart placement (default is on)
+ * Fix MoveToCenter in Xinerama (TwinView) setups
+ * Let you lower a window without lowering its children or siblings
+ * Don't set the default Openbox icon on child windows, let them inherit the
+ icon from their parents
+ * Fix Onyx themes - fade out disabled buttons
+ * Don't auto-resize windows to fit on screen if they are UserSpecified
+ Size or Position
+ * Don't activate windows which raise themselves anymore. It was a nice
+ thought, but it caused too much problems
+ * Rewrite of the actions subsystem
+ * Removed deprecated actions:
+ - ShowDesktop, UnshowDesktop (Use ToggleShowDesktop)
+ - ResizeRelativeVert, ResizeRelativeHorz (Use ResizeRelative)
+ * Combined the MoveFromEdge* actions with MoveToEdge* actions, and removed
+ the MoveFromEdge* action names.
+ * Switch desktops when moving a window and you drag it to the edge of the
+ screen (added the <screenEdgeWarpTime> option for this)
+ * Add actions to add/remove the last desktop in the client list menus
+ * Use startup notification when launching ObConf in the default root menu
+ * Remove the desktop switching dialog, as it confuses people and they don't
+ know how to turn it off to switch desktops instantly. Some other
+ incarnation may return in the future.
+ * Add optional <finalactions> option for NextWindow, PreviousWindow,
+ DirectionalFocus*, and DirectionalTarget* actions
+ * Don't focus new helper windows unless they are alone in their group. This
+ fixes gucharmap
+ * Fancy keyboard window moving:
+ - Shift-arrow will move the window to the next edge
+ - Control-arrow will move the window by only 1 pixel
+ - Arrow will move the window by a larger number of pixels
+ * Fancy keyboard window resizing:
+ - The first arrow will choose an edge to resize (then you can grow or
+ shrink the window on that edge)
+ - Hitting an arrow perpendicular to your selected edge will choose a
+ different edge to resize
+ - Shift-arrow will grow/shrink the window to the next edge
+ - Control arrow will grow/shrink the window by only 1 pixel
+ - Arrow will grow/shrink the window by a larger number of pixel
+ * Timeout when synchronously resizing a window so you can still resize busy
+ or dead application windows
+ * Let you specify keyboard shortcuts in your root menus with _. Such as
+ label="XTe_rm" would make 'r' the shortcut key.
+ * Fixes for placing windows with per-app settings:
+ - you can now place windows relative to any edge of the screen instead of
+ only the top left. (-0 uses the far edge, +0 uses the near edge. You can
+ use --0 and +-0 to specify negative numbers.)
+ - fix bug with windows being placed far off-screen occasionally
+ - allow you to place windows partially outside of the screen
+ - let per-app settings override program-specified positions (but not if it
+ is user-specified)
+ * Add user-specified margins to the configuration file
+ * Fix for transient windows in groups with transient windows of the group
+ transients. That wasn't supposed to be legible. i.e. 2 group transients
+ and 1 direct transient for one of the group transients.
+ * Don't include child/parent windows in tests for being occluded or occluding
+ other windows, since you can't raise/lower above/below them. This fixes
+ the raiselower action for windows which have children.
+ * Reduce the size of the middle gradient in splitvertical gradients. This
+ makes the split harder than it was in 3.4.2
+ * Don't include debian/ dir in releases
+ * Add resize bindings for the edges to the default hardcoded bindings
+ * Fix focus cycle indicator positioning for some windows (i.e. OpenOffice)
+ * Add the MoveResizeTo action. This is very xinerama aware! It also lets
+ you move a window to any edge of any screen.
+ * Make actions that look for edges include the dock's edges (e.g. MoveToEdge)
+ * New preferred option for the Execute action is <command> instead of
+ <execute> but the old one will still work.
+ * Fix for moving windows partially offsceen causing them to jump far away
+ * Fix for windows which are not on any monitor, to keep them on screen
+ partically too
+ * Require libstartupnotification 0.8 in configure (we use stuff that was not
+ around in some earlier versions)
+ * Grab the server when mapping windows to avoid race conditions in other
+ programs (getting MapNotify before you are visible)
+ * Handle case gracefully when ConfigureRequent for restacking specifies
+ a siblig which we aren't managing
+ * Use KeyRelease to run things from Openbox menus, so that the release
+ does not go to the focused window (causing the new window to not get
+ focus)
+ * Add new actions for controlling window decorations:
+ - Decorate, Undecorate
+ * Fix so that the values for per-app settings are not case sensitive
+ * Fix crash with very small windows
+ * Fix resize resistance for windows with aspect ratio limits
+ * Let you resize windows with aspect ratio limits in the vertical directions
+
+3.4.2:
+ * Add ability to set the bevel strength in themes
+ * Fix for focusLast with omnipresent windows
+ * Cache pipe menus while the menus are open
+ * Allow non-interactive directional focus key bindings
+ * Change how nested interactive key bindings work (it stays in the key chain,
+ like chroot, until you end the interactive action)
+ * Fix for gtk apps trying to get focus - mostly nullify focus stealing
+ prevention (Fixes Tilda)
+ * Send ConfigureNotify always on ConfigureRequest (Previous emacs fix
+ makes this possible now)
+ * Fix RelativeResize action for right/bottom edges
+ * Remove SCIM from the default autostart.sh - it has caused problems with
+ some (buggy) panels, such as pypanel
+ * Fix the titlebar changing to non-focused when iconifying a window with
+ animation
+ * Use the new osd theme hints for the dock, rather than the titlebar hints
+ * Fix rare crash when menu is hidden while browsing it
+ * End move/resize more cleanly when focus moves during a move/resize
+ * Don't focus new windows when browsing a menu or moving/resizing a window,
+ just flash the window instead, so that the menu/move/resize doesn't have to
+ cancel.
+
+3.4.1:
+ * Add Vietnamese translation
+ * Add Japanese translation
+ * Keep new transient windows from not getting focus when any related window
+ has focus already
+ * Change behavior of underMouse option - make focus go under the mouse when
+ no window has focus anymore, rather than to the last used window
+ * Make on-screen display dialogs (such as the Alt-Tab dialog) appear on the
+ active monitor when using Xinerama (a.k.a. TwinView)
+ * Fix for window stacking - let you lower sibling transient windows behind
+ eachother
+ * Fix directional focus actions when used without a dialog
+ * Fix race that could prevent ObConf from setting the desktop names correctly
+ * Add new theme options:
+ - window.active.button.toggled.image.color has been split into:
+ - window.active.button.toggled.unpressed.image.color
+ - window.active.button.toggled.pressed.image.color
+ - window.active.button.toggled.hover.image.color
+ - window.inactive.button.toggled.image.color has been split into:
+ - window.inactive.button.toggled.unpressed.image.color
+ - window.inactive.button.toggled.pressed.image.color
+ - window.inactive.button.toggled.hover.image.color
+ - window.active.button.toggled.bg has been split into:
+ - window.active.button.toggled.unpressed.bg
+ - window.active.button.toggled.pressed.bg
+ - window.active.button.toggled.hover.bg
+ - window.inactive.button.toggled.bg has been split into:
+ - window.inactive.button.toggled.unpressed.bg
+ - window.inactive.button.toggled.pressed.bg
+ - window.inactive.button.toggled.hover.bg
+ * Add new Onyx and Onyx-Citrus themes
+ * Updated Mikachu theme using new theme options
+ * Improvements to splitvertical gradients - Add a small gradient in the
+ middle between the upper and lower gradients
+ * Fix another emacs event storm, don't configure the window when it sets its
+ WM_NORMAL_HINTS unless we have to
+ * Fix for focus cycling to helper windows - Don't count dialogs as a sibling,
+ so they don't stop you
+ * Allow the use of negative numbers in per-app settings position to place a
+ window relative to the right/bottom edges of your monitor (taking the
+ struts (panels) into account)
+ * Fix focus moving irrationally when focusLast was off
+
+3.4:
+ * Add Indian Bengali translation
+ * Updated Italian translation
+ * Small updates to Czech, Estonian, Norwegian, and German translations
+ * Removed out-of-date Japanese translation (ja.po)
+ * Removed out-of-date Croatian translation (hr.po)
+ * Allow parentrelative theme textures to have borders, bevels and interlacing
+ * Add new theme elements: window.active.title.separator.color and
+ window.inactive.title.separator.color (note that globbing might set these
+ properties when you didnt mean to)
+ * Add new theme elements: osd.border.width, osd.border.color, osd.bg,
+ osd.label.bg, osd.label.text.color, osd.label.text.font, osd.hilight.bg,
+ osd.unhilight.bg
+ * Add new underMouse focus option
+ * Rename new theme element menu.items.activedisabled to
+ menu.items.active.disabled for consistency with other elements
+ * Improvements to smart placement - especially with xinerama (Twinview)
+ * Fixes for focus with omnipresent windows - Allow focus to fallback to them
+ except during desktop switches (other mechanisms exist for that)
+ * Fix for putting child windows (transients) on all desktops
+ * Changes to how Shift/Control/CapsLock are used
+ * Fix focus fallback when windows are not visible (showing the desktop, or
+ iconic windows)
+ * Fix bug #3196 - Unable to alt-tab away from full-screen windows
+ * Fix crash in parsing empty xml fields
+ * Fix left and right contexts resizing the wrong way in the example mouse
+ focus config file
+ * Fix so dock doesn't auto-hide when the mouse is inside it (over top of a
+ dock app)
+ * Allow new utility windows to gain focus like normal windows
+ * Add workarounds for Java Swing applications (Extra ConfigureNotify events)
+ * More fixes for modal window usability (Deiconify the modal window when you
+ try to focus its parent)
+ * Fixes to focus tracking, especially with iconify animation
+ * Changes to libobrender's API for ObConf 2.0 (RrThemeNew)
+ * Check for libxml >= 2.6.0 when compiling
+ * Fix for Openoffice windows (Static window gravity)
+ * Fix for hovering over window buttons while a menu is open
+ * Make the Escape key close all levels of a menu, not just one
+ * Change default drag threshold to 8px (Same as in XSettings)
+ * Fixes for windows restacking with TopIf, BottomIf, or Opposite modes.
+ Only compare with valid windows
+ * Fix for actions which find other window edges. Find all windows, and only
+ windows which are on the right desktop
+ * Improve RaiseLower action (Not lower when it doesn't need to move the
+ window)
+ * Fix for the menu hideDelay (Don't hide immediately when you click to open
+ the menu), make it work reliably
+ * Make focusLast default to true when not present in the config file
+
+3.3.995:
+ * Fix bug introduced in 3.3.993 that prevented you from raising focused
+ windows above fullscreen windows
+ * Fix bug introduced in 3.3.993 that made the desktop window keep focus
+ when changing desktops
+ * Add Arabic translations
+ * Added Control-Alt-Up/Down bindings to the mouse focus example rc.xml
+ * Don't raise on clients in the client context in the mouse focus example
+ rc.xml
+ * Fixes for when to focus new windows - improve keeping transient windows
+ from stealing focus, and allow new windows to get focus when any relative
+ window is focused
+ * Fixes for frame decorations layout
+ * Fixes for forcing window sizes to fit on the screen. Don't resize windows
+ if they are a user-specified size (USSize) or if they are non-normal
+ windows. (Fix Rox pinboard mapping across multiple monitors)
+ * Centre window icons when they are not square
+ * Fixes for Java windows behaving badly - Java makes all kinds of
+ non-standard-compliant assumptions which we are forced to meet
+ * Changed startup notification timeout from 30 to 20 seconds
+ * Fix possible crashes and misbehaviors with new focus cycling dialogs
+ * Recreate enter events when Press mouse actions move windows (e.g. Lower
+ action) for focus-follows-mouse
+ * Update Slovak translation
+ * Highlight the first menu entry when there is a separator/header at the top
+ * Only highlight the first menu entry when the menu is opened with a key
+ binding
+ * Fix crash when you press enter with nothing selected in the menu
+ * Fix for ignoring an unmap event after restart
+ * Fixes for setting the clients' colormaps
+ * Change focusLast option behavior to what it was in 3.3.1. It only affects
+ changing desktops.
+ * Add comments to the default rc.xml files about the various focus options
+ * Fix focus bug where a window had the keyboard grabbed when it unmapped
+ (Firefox gmarks extension)
+ * Update Catalan translation
+ * Fix alignment of the titlebar elements when the window icon is placed to
+ the right of the label
+ * Improve placement of child (transient) windows. When their parent is
+ omnipresent, open the child on the current desktop only.
+ * When a window has modal children on another desktop and you try to focus
+ it, bring the modal child to the current desktop to focus it instead
+ * Let you focus cycle to windows with a modal child on another desktop
+ * Fix for not seeing windows change their partial strut
+ * Give iconified windows a faded icon in the focus cycling (Alt-Tab) dialog,
+ and in the client list menus
+ * Make it more difficult to select items by accident in parent menus when it
+ changes direction. Moving the mouse only selects menu entries for a few
+ levels of open menus. Mouse clicks will select menu entries further down.
+ * Add Simplified Chinese translation
+ * Set the current theme in the OB_THEME root property for other applications
+ * Notify ksplash when we have started up if dcop is installed
+ * Allow you to resize the window on the client padding as well as the outer
+ border
+ * Fix to show the focus cycle indicator when focus cycling and there is only
+ 1 valid target
+ * Centre splash screens on one monitor
+ * Don't centre parent-less utility/menu/toolbar windows on the screen
+ * Remove the bottom decorations for fully maximized windows
+ * Fix crash when reconfiguring and no themes can be found
+
+3.3.994:
+ * Fix a bug introduced in .993 that prevented you from switching desktops
+ and caused general mayhem
+
+3.3.993:
+ * Update for Russian translation
+ * Added Dutch translation
+ * Fix for choosing badly sized icons from the window for the window cycling
+ dialog (and other things). (Fixes Pidgin icon)
+ * Open child (transient) windows on the current desktop if they don't have
+ a direct parent, or if the parents are not all on some other desktop.
+ (Fixes Pidgin and Gimp dialogs appearing on the wrong desktop)
+ * Make windows fit within the available space when they are first created,
+ if they are not user-positioned. (Fix xpdf making itself bigger than the
+ space inside the struts, and hiding behind them).
+ * Don't let child (transient) windows steal focus when the situation is
+ ambiguous
+ * Fix bevels and borders display in the titlebar
+ * Set the _NET_ACTIVE_WINDOW hint properly, when nothing is focused (This
+ is the proper fix for libwnck pagers.)
+ * Fallback focus immediately during iconify animation in all cases (was
+ missing RevertToParent case)
+ * Fix new frame elements being given 0 sizes for very small windows
+ * Send iconified windows to the bottom of the focus order so you can
+ Alt-Shift-Tab to get back what you just iconified. This makes things more
+ predictable.
+ * Show the hostname for remotely run programs in iconified titles
+ * Stop focus flicker when sending windows to a desktop and following them,
+ and when changing desktops with an omnipresent window focused
+ * Empty desktop names in the configuration file are not used, so you can set
+ a name on the second desktop without having to set one on the first
+ * Make control key work for menu accelerators like it does for mouse
+ clicking and hitting enter
+ * Add C-A-Up/Down mouse bindings to switch desktops, similar to the
+ C-A-Left/Right/Up/Down keyboard bindings
+
+3.3.992:
+ * Don't move mouse focus when the mouse is over the focus cycling dialog
+ * Fixes for positioning of frame decoration elements
+ * Update for Brazilian Portuguese translation
+ * Improvements in how decorations respond for maximized windows. (If they
+ can't be resized don't "hide" the resizing contexts)
+ * Updated Italian translation
+ * Added Czech translation
+ * Fix for applications that do weird things with modal windows (Make sure
+ you can focus cycle to them)
+ * Updated Spanish translation
+ * Fix for programs using the old WM-spec, and not setting unused fields to
+ 0 in _NET_ACTIVE_WINDOW messages (Rox pinboard)
+ * Fixes for default rc.xml configuration file. Updated comments and comment
+ out the applications section in an xml compatible way.
+ * Update for Portuguese translation
+ * Fix support for window gravity (eg xterm -geometry -0+0)
+ * Fix for libwnck with iconify animation (Unmap the parent of the client's
+ window)
+ * Force the top left corner of new windows onto the screen area, if the
+ position wasn't requested by the user (Fix xpdf and others hiding their
+ titlebar under a panel)
+ * Fix for determining text geometry from Pango
+ * Save the desktop layout and names in the session, and restore them on next
+ log in.
+ * The &lt;desktop&gt; section from the configuration file is only used for
+ defaults, and so is only applied at the start of a new log-in. (Use ObConf
+ 1.6.2 to change the config file for these options, and have them set
+ immediately, if you don't use a session manager to save them.)
+ * Fix updating and crashing in the old split client list menu
+ * Translate the default desktop names
+ * Update Taiwanese Chinese translation
+ * Less restrictions of behavior based on window type (Let you maximize
+ dialogs and utility windows)
+ * Fix for focus tracking when Press mouse actions are used
+ * Fix bug which let the openbox-*-session scripts take command-line arguments
+ * Better detection of legacy fullscreen windows vs. undercorated windows
+ that the user has maximized
+
+3.3.991:
+ * Added Brazilian Portuguese translation
+ * Added a rough Italian translation
+ * Added Estonian translation
+ * Update Polish translation
+ * Make focus stealing prevention a little less intrusive, especially with
+ default config
+ * Updates for Swedish translation
+ * Fixes for decorations of maximized windows
+ * Fix so that clicking in the corner of a maximized window presses the button
+ * Don't reload the session and reapply it when restarting
+ * Fix for losing the original size of a window that mapped with maximized
+ state (or on restart)
+ * Let you move splash type windows
+ * Fix for window placement using wrong dimensions
+ * Stop xemacs ConfigureNotify storm
+ * Fix bug stopping panels from moving themselves off-screen to hide
+ themselves
+ * Fix for GNOME/Openbox and openbox-gnome-session to use a new session name,
+ because GNOME won't let you replace the window manager in the saved session
+ * Don't restore session state when multiple windows have the same
+ identifiers, because we can't tell them apart then (Firefox/Thunderbird)
+ * Small fixes to build system to make it more reliable
+ * Fix for what manpage files are distributed in the source code, so they
+ will not be pre-built with the wrong info
+ * Fix pkg-config files for compiling things against Openbox libraries
+
+3.3.990:
+ * Wildcard matching for per-application (per-window) settings. Let you match
+ multiple rules for a window. (Props to Mark Pustjens for code to do this.)
+ * Added startup notification support to the menus and key/mouse bindings
+ with <startupnotify>. It's an option for execute actions.
+ * New combined client menu, accessed with
+ <action name="ShowMenu"><menu>client-list-combined-menu</menu></action>
+ * No menu titles - instead added separators with labels to be used as menu
+ headers anywhere in menus
+ * Improved client menu - placement, the options in it, and added an icon for
+ "All Desktops"
+ * Keyboard shortcuts in menus, by highlighted letter or by the first letter
+ * Menus dont jump around, are always placed on screen
+ * Menus larger than can fit on the screen are broken into a submenu at the
+ bottom titled "More..."
+ * Disabled menu items can be selected now (not used though, of course)
+ * Using ()'s instead of a line separator to signify iconified windows in the
+ client list menus
+ * New Root context for mouse bindings
+ * New "alt-tab" focus switching dialog
+ * openbox-session command and log-in option which provides the autostart
+ capability.
+ * SCIM support in the default Openbox session launched by the
+ openbox-session command
+ * Many code paths made significantly faster
+ * Sections of code rewritten to be more reliable, such as session
+ save/restore and input focus handling
+ * Handle the X server's clock going backwards, without locking up. Sometimes
+ the server can still lock up though, it is not recommended to do this!
+ * Better handling of window types such as docks, utility windows, tool bar
+ windows. Clicking to focus these windows is done more intelligently. Dock
+ windows no longer get focus when you click them, so that you don't have
+ focus flickering when you use a dock window with a taskbar.
+ * Better language support through Pango, which is now mandatory, and the
+ code for Pango has been greatly improved and optimized.
+ * Support for windows that use true 32-bit transparency
+ * Resize grips (with mouse binding contexts) for the top, left, and right of
+ the window frame
+ * Smarter about keeping windows on the screen without invading user's
+ capabilities
+ * Add MoveFromEdge* actions corresponding to MoveToEdge* but aligns far
+ edges instead of near edges, so if you have two overlapping windows you
+ can easily put them side by side.
+ * Better choice of mouse cursors for startup notification
+ * Focus stealing prevention
+ * Improve window placement across multiple Xinerama monitors
+ * Support 8-bit truecolor visuals (such as in vncserver)
+ * Better, more friendly support for omnipresent windows
+ * Full support for EWMH 1.4-draft2
+ * Iconify/restore animation !
+ * Improved support for running openbox with multiple screens (non-xinerama)
+ - won't steal focus from other screens, and let you focus a screen by
+ clicking on the desktop (root window).
+ * Support for more cursors through XCursor, means that startup notification
+ can use a pointer+watch cursor if you are using an XCursor theme
+ * Fully maximize terminal windows (and any resized-by-increment window)
+ * Show the hostname of clients running on remote computers in the titlebar
+ * Add <panels> and <desktop> options to NextWindow, PreviousWindow,
+ DirectionalFocusNorth etc..
+ * Synchronize resizing with the application when possible
+ * Smarter subdivision of windows into 9 sections for resizing
+ * Set default icon on windows so applications can agree on an icon
+ * Better support for programs that are on more than one desktop
+ (multiple firefox windows for example) - move to the desktop when you're
+ using the application and it opens a window on another desktop.
+ * Chrooted key chains, and key quoting through chroots
+ * New BreakChroot action to break out of the current chroot
+ * New keychains popup, to show where you are in a keychain
+ * Improved show-desktop mode. When you open a new window, the old windows
+ are not all restored
+ * Add <allDesktops> option to NextWindow and PreviousWindow. The desktop
+ name will be shown in the focus cycling popup
+ * Dynamically size the window and desktop dialogs. Desktop layouts that
+ aren't horizontal look better in the desktop cycling dialog.
+ * Support for legacy fullscreen applications (that don't use EWMH) such as
+ VLC
+ * Session support for old clients that do not use the session management
+ protocol
+ * Desktop setup is only changed on startup, so as to not overwrite settings
+ from other apps like pagers
+ * Take advantage of ksmserver (KDE Session Manager) features
+ * KDE/Openbox and GNOME/Openbox options for display manager GDM/KDM. Use
+ openbox-gnome-session and openbox-kde-session to run a GNOME or KDE
+ session with Openbox as the window manager.
+ * Titlebar buttons autohide when hiding won't move other buttons (removed
+ the hideDisabled option from config file)
+ * Support user time window hint, which is good for laptops power usage
+ * When legacy windows raise themselves, interpreate as wanting to be
+ activated. Makes Firefox more pleasant.
+ * Don't show handles on windows that cant resize for more extreme visual
+ minimalism
+ * Let you move maximized windows between Xinerama monitors
+ * Improve application awareness of focus, e.g. Kopete and Firefox, so they
+ know when they are or aren't focused, and which window in their
+ application is focused
+ * Improvements in the distributed themes
+ * Add new Clearlooks and Clearlooks-Olive themes by John McKnight
+ * Fonts are no longer loaded from theme. They are loaded from the rc.xml
+ config file. Font shadow settings are still loaded from the theme though.
+ 5 fonts to be set: ActiveWindow InactiveWindow, MenuHeader, MenuItem and
+ OnScreenDisplay
+ * Distributed theme names have been renamed with Capitalization
+ * menu.items.activedisabled.text.color
+ * menu.border.color (note that globbing might set these properties when
+ you didnt mean to)
+ * window.active.border.color (note that globbing might set these
+ properties when you didnt mean to)
+ * window.inactive.border.color (note that globbing might set these
+ properties when you didnt mean to)
+ * menu.border.width
+ * Guarantee that titlebar buttons are even sized, so even sized icons
+ (button masks) will be centered
+ * Default titlebar button icons (as xbms) installed to /usr/share/doc/openbox
+ * New mouse binding contexts Left, Right, TLCorner, TRCorner and Top. These
+ need bindings in config file to be useful. Also new context Bottom which
+ is synonym for Handle.
+ * W is no longer hard coded to Mod4. It is now a shortcut for Super, which
+ is usually bound to Mod4. xmodmap will show your mapping.
+ * The per-app setting <head> has been renamed to <monitor>
+ * Can use "default" for anything in per-app settings
+ * Removed <edges_hit_layers_below> option from config file
+ * --reconfigure command line option
+ * --restart command line option
+ * Closed bug #886 - Reference point for client-menu
+ * Closed bug #898 - Add option for OB2 style menu behaviour
+ * Closed bug #952 - Focus sometimes gets lost when closing windows
+ * Closed bug #1006 - duplicate window title numbering is so-so
+ * Closed bug #1080 - menus dont update when using ctrl to keep them open
+ * Closed bug #1082 - doing window stuff while changing focus is mad slow
+ * Closed bug #1106 - Openbox resize problems with Beep Media Player and some
+ websites using Mozilla
+ * Closed bug #1146 - minimized windows dont unminimize from fspanel if ob
+ restarted
+ * Closed bug #1409 - Window stacking is messed up when dealing with multiple
+ nested dialog boxes
+ * Closed bug #1526 - Incorrect handling of utility windows
+ * Closed bug #1846 - Patch for theme overrides
+ * Closed bug #1974 - Limit the number of resizes per second to make window
+ resizing smoother
+ * Closed bug #2060 - Apps disappear when using composite extension
+ * Closed bug #2221 - _NET_WM_STATE_(ABOVE|BELOW) is being inherited
+ * Closed bug #2250 - openbox ignores size request ?
+ * Closed bug #2251 - support for legacy ""fullscreen""
+ * Closed bug #2254 - segfault in render/render.c on sendtodesktop [endian?]
+ * Closed bug #2255 - segmentation fault on 8-bit truecolor visual
+ * Closed bug #2258 - long menus truncated
+ * Closed bug #2311 - Does not display the pressed state while a button in the
+ frame is toggled
+ * Closed bug #2321 - ResizeRelative -up and -left makes window move when at
+ min_size
+ * Closed bug #2483 - Pinned windows raise to top on all desktops when they
+ become focused on just one desktop
+ * Closed bug #2491 - Client List by App instead of by Desktop
+ * Closed bug #2551 - Request to be able to cycle docks (e.g.
+ * Closed bug #2659 - Menu header display should be configurable
+ * Closed bug #2769 - temporarily chroot the keychain tree
+ * Closed bug #2841 - Changes to the keepborder option aren't applied when
+ reconfiguring
+ * Closed bug #2850 - Openbox gives black backgrounds to windows using the
+ new 'rgba' or 'argb' visual
+ * Closed bug #2865 - openbox 3.3 is not C89 compatible
+ * Closed bug #2872 - Focus issues with Eclipse and Firefox
+ * Closed bug #2878 - Openbox crash when used with vncserver
+ * Closed bug #2897 - Openbox shouldn't redraw the whole screen everytime
+ when minimizing windows.
+ * Closed bug #2898 - Patch adding advanced keyboard support in popup menus.
+ * Closed bug #2982 - tilda doesn't work propertly
+ * Closed bug #3034 - themeupdate.py seems to bee broken
+ * Closed bug #3045 - RrPaint in obrender should be split for easier use by
+ external apps
+ * Closed bug #3050 - 'syscrash' theme update
+ * Closed bug #3051 - When closing windows
+ * Closed bug #3053 - Updates de.po for German Localisation
+ * Closed bug #3059 - Feature Request: NextWindowForAllWorkspaces
+ * Closed bug #3063 - extensions_xinerama_screens() leaks memory if Xinerama
+ is enabled
+ * Closed bug #3064 - slist_path_add() might leak memory
+ * Closed bug #3068 - Multimonitor focus problems
+ * Closed bug #3081 - window doesn't appear
+ * Closed bug #3092 - Config parser expects wrong values of "layer" in
+ per-application settings
+ * Closed bug #3094 - smart placement should Ignore maximized windows
+ * Closed bug #3100 - Netwmpager is configured with layer = "below" but OB
+ is ignoring it
+ * Closed bug #3101 - Openbox won't compile on CentOS 4.4
+ * Closed bug #3102 - svn-6175 windows come not back in the foregroud
+ * Closed bug #3106 - Fullscreened mplayer loses focus when switching to
+ another desktop and back
+ * Closed bug #3108 - After revision 6437 the desktop dialog window stays on
+ screen
+
+3.3.1:
+ * Fix panels getting a border with keepBorder turned on.
+ * Fix a crash in mirrorhorizontal when drawing a surface with width 1.
+
+3.3:
+ * Add a showDelay option for the dock.
+ * Fixed onscreen-keeping code which broke a bit in -rc2.
+ * Fix incorrect handling of window properties on 64 bit arches.
+ * Fix pixelsize being the same as size for pango, now it is correct.
+ * Fix drawing of icons taller than wide, eg gimp.
+ * Add a 'mirrorhorizontal' gradient, like horizontal but mirrored in the
+ center and 'splitvertical' which is like vertical but nonlinear in a fancy
+ way.
+ * Translations for de, hr and zh_TW added.
+ * Add initial per-app settings support.
+ * Fix some outstanding issues with 64-bit support.
+ * Add an optional delay for displaying submenus so you can move the mouse
+ over some entries without flashing each submenu.
+ * Plug some minor memleaks and fix minor bugs.
+ * Changed algorithm for downscaling window icons.
+ * New themes!
+ * Disable support for libstartup-notification by default, it doesn't seem
+ to work well.
+ * Allow disabling of duplicate window numbering by option titleNumber in
+ theme section.
+ * Add moverelative and resizerelative actions.
+ * Option middle for menus that make submenus be centered instead of aligned
+ by the top edge to the parent entry.
+ * Fix transient (ie dialog) windows being placed offscreen if the parent
+ is close to the edge of the screen.
+
+3.3-rc2:
+ * Fixed some typos and errors in rc.xsd
+ * Add the noStrut option to the dock (to allow maximizing windows over it),
+ useful when it is not in the above layer.
+ * Fix transparent terminals not updating when using ToggleDecor.
+ * Yet more poking at the onscreen-keeping code, should now do the right
+ thing always.
+ * Add an option to hide disabled buttons instead of showing them as disabled.
+ * Hopefully fixed all the remaining pango issues. The new 1.10 release also
+ fixes shadows again.
+ * I think the lib linking in Makefile.am is right now, please tell me if it
+ isn't.
+ * Added a finnish translation and some minor updates to others.
+ * Fixed configure.ac so --enable-pango doesn't disable pango support or the
+ other way around, make enabled be default to get some extra testing.
+
+3.3-rc1:
+ * Poked around a bit in the code that keeps windows onscreen, if you
+ are using xinerama, please let me know if things are better/worse/same.
+ * Add support for pango, disabled by default due to a bug in pango 1.8 that
+ breaks shadows, it works fine with 1.6.x though. Poke the pango developers
+ here http://bugzilla.gnome.org/show_bug.cgi?id=169622 if you want this
+ to be fixed.
+ * Made edge resist and Send/MoveToEdge not hit windows at layers below
+ the current one, add option to disable this behaviour.
+ * Fixed directional focus not getting trapped in omnipresent windows
+ * Fixed focus actions when bound to the mouse, normal focus cycle is always
+ linear and directional focus is totally disabled, it makes no sense
+ anyway, just move the mouse.
+ * Added an option for the raise/lower/raiselower/unshaderaise/shadelower
+ options called group that lets you bind stuff to raise/lower the whole
+ window group. <action name="raise"><group>yes</group></action>
+ * Add various new actions. I haven't updated the web docs yet, but I think
+ I managed to get all of them listed in rc.xsd.
+ * Bugs 1783, 1812, 1863, 1905, 2005, 1957 fixed.
+ * Some translations added. (ca, no, pl, ru, fr)
+
+3.2:
+ * Added spanish and swedish translations
+ * Changed "Unnamed Desktop" to "Desktop n" where n is the actual
+ number of the desktop
+ * Hopefully no more of the hanging X totally bug
+ * the fullscreen property now overrides aspect hints, all programs i've
+ tried that use it (mplayer and tvtime) handle the aspect internally
+ anyway, this just makes sure you have black borders all the way to the
+ edge. If this breaks anything for you, let me know.
+ * Added M as an alias for Mod3 in the same manner that W is an alias for Mod4
+ * Added probably too many options and actions
+ * ToggleDockAutohide to toggle the autohide option at runtime,
+ doesn't save it to the config
+ * keepBorder option to set if you want the 1-pixel border when you
+ toggleDecorations
+ * added popupShow and popupPosition to control where and when the
+ move/resize-popup is shown, these go in the resize section of the config
+ popupPosition can be Top or Center, popupShow can be Always, Never or
+ Nonpixel (this is the default).
+ * Added the xorStyle option to change the behaviour of menus along the bottom
+ edge, note that the default is true
+ * warpPointer option in the menu section to control if the mouse cursor is
+ to be warped by openbox when the menu moves to keep on screen to keep the
+ cursor in the same place relative to the menu. (I think i got that right)
+ * Added back the focusLast option (this controls what happens when you
+ change desktops and have followMouse on)
+ * Added an option to set which desktop to start on when you start openbox,
+ firstdesk, for example you might like to set it to 5 if you have a 3x3
+ grid to start in the middle.
+ * I think that was everything, have fun
+
+3.1:
+ * Added japanese translation (bug #1029)
+ * Closed bug #1060 (modal unfocused window should be placed above
+ focused parent)
+ * Closed bug #1012 (Openbox stalls gnome session startup after
+ --replace)
+ * Closed bug #1073 (The Desktop action doesn't work)
+ * Closed bug #1023 (config_focus_raise declared as guint)
+ * Closed bug #1019 (Strange behavior with focus:followMouse and
+ RaiseLower action)
+ * Closed bug #1007 (use g_snprintf in openbox/session.c instead of
+ sprintf)
+ * Closed bug #1001 (gimp crashes openbox)
+ * Closed bug #1005 (import -frame leaves windows in a temporary
+ "on top" state)
+ * Closed bug #881 (Programs started with X become zombies upon
+ shutdown.)
+ * Closed bug #1025 (desktopnext/previous and desktopleft/right dont
+ quite get along)
+
+3.0:
+ * Closed bug #908 (sometimes some weird grab is left)
+ * Closed bug #945 (xawtv overlay visible on every desktop)
+ * Closed bug #944 (remove restarting/reconfiguring messages from
+ openbox.c)
+ * Closed bug #946 (use g_strsplit in parse.c:split_paths())
+ * Closed bug #943 (consistent glib type (gint, gchar, etc.) usage)
+ * Closed bug #951 (stale openbox session files)
+ * Closed bug #947 (Sloppyfocus can yield two windows at the same time
+ with focus (!))
+ * Closed bug #948 (Gets stuck in 'move' mode if mouse is let up while
+ dragged away from title bar)
+ * Closed bug #949 (dock above gnome panel behaviour)
+ * Closed bug #902 (Openbox raises gDesklets when clicked)
+ * Closed bug #904 (ToggleMaximizeHorz & Vert dont always toggle)
+ * Closed bug #907 (window focus is not properly indicated)
+ * Closed bug #919 (Temporary raise window when alt tabbing)
+ * Closed bug #921 (focus rectangle stays on the screen after modifier
+ release)
+ * Closed bug #925 (Invalid option: '--sm-save' & '--sm-disable')
+ * Closed bug #926 (problem with client-list-menu, destkop switching,
+ and sticky client)
+ * Closed bug #927 (disappearing windows, crash with I-merry theme)
+ * Closed bug #929 (crash (signal 11) when activating Reconfigure)
+ * Closed bug #940 (actions dont work on desktops with no client
+ windows)
+ * Closed bug #943 (consistent glib type (gint, gchar, etc.) usage)
+ * Closed bug #957 (openbox crashes when exiting out of OpenOffice
+ Impress)
+ * Closed bug #960 (look for dupes in xdg paths when setting up slists?)
+ * Closed bug #961 (Menu causes core dump)
+ * Closed bug #963 (wrapper for mkdir() in parse.c)
+ * Closed bug #964 (free GErrors in action.c, openbox.c)
+ * Closed bug #965 (add message for chdir() failure in openbox.c)
+ * Closed bug #966 (list->next/prev to g_[s]list_next/previous, list !=
+ NULL to list
+ * Closed bug #971 (typo in code)
+ * Closed bug #974 (use g_getenv() in parser/parse.c)
+ * Closed bug #975 (void * -> gpointer)
+ * Closed bug #976 (use g_strerror() in openbox/session.c)
+ * Closed bug #978 (disable entry to go to desktop if it is the current)
+ * Closed bug #979 (request for version check in ob libraries)
+ * Closed bug #944 (remove restarting/reconfiguring messages from
+ openbox.c)
+ * Closed bug #968 (rc.xml refers to a non-existant "debian" menu file)
+
+3.0-rc4:
+ * Closed bug #921 (focus rectangle stays on the screen after modifier
+ release)
+ * Closed bug #927 (disappearing windows, crash with I-merry theme)
+ * Fix crash when restoring a session with a maximized window
+ * Fix leak of RrColors in menus
+ * Closed bug #929 (crash (signal 11) when activating Reconfigure)
+ * Allow focus to be moved around by clients (for WM_TAKE_FOCUS to
+ work as it is meant to). This means fullscreen apps like wine and
+ bzflag work now
+ * Reversed the default directions for the mouse wheel desktop
+ switching actions
+ * Don't wrap around in the default keyboard desktop switching bindings
+ * Place parentless dialogs so they avoid always-on-top windows
+ * Ignore fullscreen windows in placing new ones
+ * Closed bug #940 (actions dont work on desktops with no client
+ windows)
+
+3.0-rc3:
+ * Closed bug #904 (ToggleMaximizeHorz & Vert dont always toggle)
+ * Allow resizing of windows in only vertical/horizontal directions
+ (via Alt-MiddleClick-Drag with the default bindings)
+ * Added intelligence to window cycling and raise/lowering based on
+ window types
+ * Closed bug #902 (Openbox raises gDesklets when clicked). This has the
+ side effect that panels which do not set the ABOVE state (most panels
+ curerntly around) will no longer be kept always on top. You can set the
+ ABOVE state yourself by using the client menu (Alt-RightClick in the
+ default bindings) and selecting the Always on top Layer
+ * Closed bug #907 (window focus is not properly indicated)
+ * Added a focus indicator during focus cycling that is always visible
+ instead of just moving the focus hilight around.
+
+3.0-rc2:
+ * Closed bug #885 (raiseOnFocus is flawed)
+ * Closed bug #887 (Mouse-wheel scrolling only changes 1 desktop when
+ using "dialog" option)
+ * Closed bug #888 (some random coredump)
+ * Closed bug #889 (focus follow mouse not following)
+ * Closed bug #892 (client_under_pointer() doesn't check DESKTOP_ALL)
+ * Avoid the focus flicker when switching desktops
+ * Free the XML DOM trees properly, fixes mem leak
+
+3.0-rc1:
+ * Closed bug #875 (RaiseLower fails to funtion on windows with dialogs)
+ * Added the <moveButton> option for moving dock apps around inside the
+ dock. (Closes bug #798)
+ * Made the "Desktop" action show the pager/desktop dialog
+ * Add the <dialog> option to the Desktop and Focus switching actions
+ * No longer iconify fullscreen windows when they lose focus (Closes
+ bug #876)
+ * When not using focusLast, when a focused transient is closed, focus
+ will still fallback to its parent or transient siblings
+ * Improved intelligence for focus cycling with modal transients
+ * Removed the focusLast option
+ * Added the placement-policy option to select if windows are placed
+ under the mouse pointer
+ * Children inherit their window icon from their parent
+ * Closed bug #878 (minimal resizing of mplayer makes openbox crash)
+ * Fix case where opening a menu would leave Openbox in a locked state
+ * Upstream updates for TheBear theme
+
+3.0-beta6:
+ * Added _OB_WM_STATE_UNDECORATED hint, which can be read and controlled
+ by external applications
+ * Closed bug #874 (libtool won't install libobrender.la)
+ * Launch ObConf when selecting GNOME's Desktop Preferences for Windows
+ * Focus new children always if their direct parent is focused
+
+3.0-beta5:
+ * Initial release.
diff --git a/COMPLIANCE b/COMPLIANCE
new file mode 100644
index 0000000..b5fe279
--- /dev/null
+++ b/COMPLIANCE
@@ -0,0 +1,78 @@
+EWMH Compliance Document:
+==========================
+
+Listed below are all the NetWM (or EWM) hints decided upon on freedesktop.org
+and Openbox's current level of compliance with the spec. Beside each hint is
+the version of the spec which Openbox is compliant up to for the hint.
+
+(
+ compliance :
+ - = none,
+ / = partial,
+ + = complete,
+ * = Openbox is compliant, but something else needs checking
+ ? = unknown
+)
+
++ _NET_SUPPORTED (1.3)
++ _NET_CLIENT_LIST (1.3)
++ _NET_NUMBER_OF_DESKTOPS (1.3)
++ _NET_DESKTOP_GEOMETRY (1.3)
+ Openbox doesn't support large desktops so these just match the
+ screen size.
++ _NET_DESKTOP_VIEWPORT (1.3)
+ Openbox doesn't support large desktops so these are just (0, 0).
++ _NET_CURRENT_DESKTOP (1.3)
++ _NET_DESKTOP_NAMES (1.3)
++ _NET_ACTIVE_WINDOW (1.3)
++ _NET_WORKAREA (1.3)
++ _NET_SUPPORTING_WM_CHECK (1.3)
++ _NET_VIRTUAL_ROOTS (1.3)
+ Openbox does not use virtual roots, so this is not needed.
++ _NET_DESKTOP_LAYOUT (1.3)
++ _NET_SHOWING_DESKTOP (1.3)
++ _NET_CLOSE_WINDOW (1.3)
++ _NET_MOVERESIZE_WINDOW (1.3)
++ _NET_WM_MOVERESIZE (1.3)
++ _NET_WM_NAME (1.3)
++ _NET_WM_VISIBLE_NAME (1.3)
++ _NET_WM_ICON_NAME (1.3)
++ _NET_WM_VISIBLE_ICON_NAME (1.3)
++ _NET_WM_DESKTOP (1.3)
++ _NET_WM_WINDOW_TYPE (1.3)
+ Openbox does not let windows change this hint after mapping.
++ _NET_WM_STATE (1.3)
++ _NET_WM_ALLOWED_ACTIONS (1.3)
++ _NET_WM_STRUT (1.3)
++ _NET_WM_STRUT_PARTIAL (1.3)
+ Openbox uses these to create per-monitor struts in Xinerama setups.
++ _NET_WM_ICON_GEOMETRY (1.3)
++ _NET_WM_ICON (1.3)
+- _NET_WM_PID (1.3)
+ Openbox does not currently kill processes.
+- _NET_WM_HANDLED_ICONS (1.3)
+ Openbox does not display icons for iconic windows.
++ _NET_WM_USER_TIME (1.3)
+- _NET_WM_USER_TIME_WINDOW (1.4)
+- _NET_WM_PING (1.3)
+ Openbox doesn't look for hung processes at this time.
++ _NET_FRAME_EXTENTS (1.3)
++ _NET_WM_STATE_DEMANDS_ATTENTION (1.3)
++ _NET_RESTACK_WINDOW (1.3)
++ _NET_WM_SYNC_REQUEST (1.3)
++ _NET_WM_FULL_PLACEMENT (1.4)
++ _NET_WM_MOVERESIZE_CANCEL (1.4)
++ _NET_REQUEST_FRAME_EXTENTS (1.3)
++ _NET_WM_ACTION_MOVE (1.3)
++ _NET_WM_ACTION_RESIZE (1.3)
++ _NET_WM_ACTION_MINIMIZE (1.3)
++ _NET_WM_ACTION_SHADE (1.3)
+- _NET_WM_ACTION_STICK (1.3)
+ Openbox does not do large desktops, so no sticky state is needed.
++ _NET_WM_ACTION_MAXIMIZE_HORZ (1.3)
++ _NET_WM_ACTION_MAXIMIZE_VERT (1.3)
++ _NET_WM_ACTION_FULLSCREEN (1.3)
++ _NET_WM_ACTION_CHANGE_DESKTOP (1.3)
++ _NET_WM_ACTION_CLOSE (1.3)
++ _NET_WM_ACTION_ABOVE (1.4?)
++ _NET_WM_ACTION_BELOW (1.4?)
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/HACKING b/HACKING
new file mode 100644
index 0000000..e450bba
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,33 @@
+dirs:
+ openbox - core of the WM
+ render - librender, rendering routines for the WM and for apps
+ parser - libparser, for parsing config files
+
+Beware the Client.transient_for. It can be set to a !NULL value of TRAN_GROUP,
+which is not a valid pointer. You must ALWAYS check for TRAN_GROUP before
+following transient_for. However if it is transient for the group, this
+excludes other windows whom are transient for the group, and windows which
+are children of the window (infinite loops would result)!
+
+When using coordinates/sizes of windows, make sure you use the right area. The
+Client.area rect is the reference point and size of the *CLIENT* window. This
+value is not what you see in any shape or form, and gravity is applied to it to
+translate it into what you see. The Client.frame.area is the actual position
+and size of the entire frame. This is usually the value you want to use, unless
+you are in client.c (probably) and adjusting/using the position or size from
+the client's perspective.
+
+Indentation
+-----------
+For openbox, we aim to have consistent coding style. Some, but surely
+not all, guidelines:
+ * use 4 space indents
+ * tabs should not appear in source files
+ * functions should have the opening and closing braces on their own
+ lines
+ * most other constructs should have braces on the same line as the
+ statement
+ * else appears on a new line, just like an if
+ * when in doubt look at the rest of the source
+ * vim users can use "set expandtab tabstop=4 shiftwidth=4
+ softtabstop=4" for some of this
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..1f7427c
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,604 @@
+SUBDIRS = m4 po
+
+docxbmdir = $(docdir)/xbm
+themedir = $(datadir)/themes
+localedir = $(datadir)/locale
+configdir = $(sysconfdir)/xdg
+rcdir = $(configdir)/openbox
+xsessionsdir = $(datadir)/xsessions
+gnomesessiondir = $(datadir)/gnome-session/sessions
+gnomewmfilesdir = $(datadir)/gnome/wm-properties
+pkgconfigdir = $(libdir)/pkgconfig
+obtpubincludedir= $(includedir)/openbox/@OBT_VERSION@/obt
+rrpubincludedir = $(includedir)/openbox/@RR_VERSION@/obrender
+pixmapdir = $(datadir)/pixmaps
+xsddir = $(datadir)/openbox
+appsdir = $(datadir)/applications
+
+theme = Clearlooks
+
+AUTOMAKE_OPTIONS = subdir-objects foreign
+
+ACLOCAL_AMFLAGS = -I m4
+
+INCLUDES = -I.
+
+check_PROGRAMS = \
+ obrender/rendertest
+
+lib_LTLIBRARIES = \
+ obt/libobt.la \
+ obrender/libobrender.la
+
+bin_PROGRAMS = \
+ openbox/openbox \
+ tools/gdm-control/gdm-control \
+ tools/gnome-panel-control/gnome-panel-control \
+ tools/obxprop/obxprop
+
+nodist_bin_SCRIPTS = \
+ data/xsession/openbox-session \
+ data/xsession/openbox-gnome-session \
+ data/xsession/openbox-kde-session
+
+dist_rc_SCRIPTS = \
+ data/environment
+
+nodist_rc_SCRIPTS = \
+ data/autostart/autostart
+
+dist_libexec_SCRIPTS = \
+ data/autostart/openbox-xdg-autostart
+
+nodist_libexec_SCRIPTS = \
+ data/autostart/openbox-autostart
+
+
+## obrender ##
+
+obrender_rendertest_CPPFLAGS = \
+ $(PANGO_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ -DG_LOG_DOMAIN=\"RenderTest\"
+obrender_rendertest_LDADD = \
+ obt/libobt.la \
+ obrender/libobrender.la \
+ $(GLIB_LIBS) \
+ $(PANGO_LIBS) \
+ $(XML_LIBS) \
+ $(X_LIBS)
+obrender_rendertest_SOURCES = obrender/test.c
+
+obrender_libobrender_la_CPPFLAGS = \
+ $(X_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(XML_CFLAGS) \
+ $(PANGO_CFLAGS) \
+ $(IMLIB2_CFLAGS) \
+ -DG_LOG_DOMAIN=\"ObRender\" \
+ -DDEFAULT_THEME=\"$(theme)\"
+obrender_libobrender_la_LDFLAGS = \
+ -version-info $(RR_CURRENT):$(RR_REVISION):$(RR_AGE)
+obrender_libobrender_la_LIBADD = \
+ obt/libobt.la \
+ $(X_LIBS) \
+ $(PANGO_LIBS) \
+ $(GLIB_LIBS) \
+ $(IMLIB2_LIBS) \
+ $(XML_LIBS)
+obrender_libobrender_la_SOURCES = \
+ gettext.h \
+ obrender/button.h \
+ obrender/button.c \
+ obrender/color.h \
+ obrender/color.c \
+ obrender/font.h \
+ obrender/font.c \
+ obrender/geom.h \
+ obrender/gradient.h \
+ obrender/gradient.c \
+ obrender/icon.h \
+ obrender/image.h \
+ obrender/image.c \
+ obrender/imagecache.h \
+ obrender/imagecache.c \
+ obrender/instance.h \
+ obrender/instance.c \
+ obrender/mask.h \
+ obrender/mask.c \
+ obrender/render.h \
+ obrender/render.c \
+ obrender/theme.h \
+ obrender/theme.c
+
+## obt ##
+
+obt_libobt_la_CPPFLAGS = \
+ $(XINERAMA_CFLAGS) \
+ $(XKB_CFLAGS) \
+ $(XRANDR_CFLAGS) \
+ $(XSHAPE_CFLAGS) \
+ $(XSYNC_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(XML_CFLAGS) \
+ -DG_LOG_DOMAIN=\"Obt\" \
+ -DLOCALEDIR=\"$(localedir)\" \
+ -DDATADIR=\"$(datadir)\" \
+ -DCONFIGDIR=\"$(configdir)\"
+obt_libobt_la_LDFLAGS = \
+ -version-info $(OBT_CURRENT):$(OBT_REVISION):$(OBT_AGE)
+obt_libobt_la_LIBADD = \
+ $(XINERAMA_LIBS) \
+ $(XKB_LIBS) \
+ $(XRANDR_LIBS) \
+ $(XSHAPE_LIBS) \
+ $(XSYNC_LIBS) \
+ $(GLIB_LIBS) \
+ $(XML_LIBS)
+obt_libobt_la_SOURCES = \
+ obt/bsearch.h \
+ obt/display.h \
+ obt/display.c \
+ obt/internal.h \
+ obt/keyboard.h \
+ obt/keyboard.c \
+ obt/xml.h \
+ obt/xml.c \
+ obt/ddparse.h \
+ obt/ddparse.c \
+ obt/link.h \
+ obt/link.c \
+ obt/paths.h \
+ obt/paths.c \
+ obt/prop.h \
+ obt/prop.c \
+ obt/signal.h \
+ obt/signal.c \
+ obt/util.h \
+ obt/xqueue.h \
+ obt/xqueue.c
+
+## openbox ##
+
+openbox_openbox_CPPFLAGS = \
+ $(SM_CFLAGS) \
+ $(X_CFLAGS) \
+ $(XCURSOR_CFLAGS) \
+ $(SM_CFLAGS) \
+ $(PANGO_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(LIBSN_CFLAGS) \
+ $(XML_CFLAGS) \
+ -DLOCALEDIR=\"$(localedir)\" \
+ -DDATADIR=\"$(datadir)\" \
+ -DCONFIGDIR=\"$(configdir)\" \
+ -DG_LOG_DOMAIN=\"Openbox\"
+openbox_openbox_LDADD = \
+ $(XINERAMA_LIBS) \
+ $(XRANDR_LIBS) \
+ $(SM_LIBS) \
+ $(GLIB_LIBS) \
+ $(X_LIBS) \
+ $(XCURSOR_LIBS) \
+ $(LIBSN_LIBS) \
+ $(XML_LIBS) \
+ $(EFENCE_LIBS) \
+ $(LIBINTL) \
+ obrender/libobrender.la \
+ obt/libobt.la
+openbox_openbox_LDFLAGS = -export-dynamic
+openbox_openbox_SOURCES = \
+ gettext.h \
+ openbox/actions/all.c \
+ openbox/actions/all.h \
+ openbox/actions/addremovedesktop.c \
+ openbox/actions/breakchroot.c \
+ openbox/actions/close.c \
+ openbox/actions/cyclewindows.c \
+ openbox/actions/debug.c \
+ openbox/actions/decorations.c \
+ openbox/actions/desktop.c \
+ openbox/actions/dock.c \
+ openbox/actions/dockautohide.c \
+ openbox/actions/directionalwindows.c \
+ openbox/actions/execute.c \
+ openbox/actions/exit.c \
+ openbox/actions/focus.c \
+ openbox/actions/focustobottom.c \
+ openbox/actions/fullscreen.c \
+ openbox/actions/growtoedge.c \
+ openbox/actions/iconify.c \
+ openbox/actions/if.c \
+ openbox/actions/kill.c \
+ openbox/actions/layer.c \
+ openbox/actions/lower.c \
+ openbox/actions/maximize.c \
+ openbox/actions/move.c \
+ openbox/actions/moverelative.c \
+ openbox/actions/moveresizeto.c \
+ openbox/actions/movetoedge.c \
+ openbox/actions/omnipresent.c \
+ openbox/actions/raise.c \
+ openbox/actions/raiselower.c \
+ openbox/actions/reconfigure.c \
+ openbox/actions/resize.c \
+ openbox/actions/resizerelative.c \
+ openbox/actions/restart.c \
+ openbox/actions/shade.c \
+ openbox/actions/shadelowerraise.c \
+ openbox/actions/showdesktop.c \
+ openbox/actions/showmenu.c \
+ openbox/actions/unfocus.c \
+ openbox/actions.c \
+ openbox/actions.h \
+ openbox/client.c \
+ openbox/client.h \
+ openbox/client_list_menu.c \
+ openbox/client_list_menu.h \
+ openbox/client_list_combined_menu.c \
+ openbox/client_list_combined_menu.h \
+ openbox/client_menu.c \
+ openbox/client_menu.h \
+ openbox/config.c \
+ openbox/config.h \
+ openbox/debug.c \
+ openbox/debug.h \
+ openbox/dock.c \
+ openbox/dock.h \
+ openbox/event.c \
+ openbox/event.h \
+ openbox/focus.c \
+ openbox/focus.h \
+ openbox/focus_cycle.c \
+ openbox/focus_cycle.h \
+ openbox/focus_cycle_indicator.c \
+ openbox/focus_cycle_indicator.h \
+ openbox/focus_cycle_popup.c \
+ openbox/focus_cycle_popup.h \
+ openbox/frame.c \
+ openbox/frame.h \
+ openbox/framerender.c \
+ openbox/framerender.h \
+ openbox/geom.h \
+ openbox/grab.c \
+ openbox/grab.h \
+ openbox/group.c \
+ openbox/group.h \
+ openbox/keyboard.c \
+ openbox/keyboard.h \
+ openbox/keytree.c \
+ openbox/keytree.h \
+ openbox/menuframe.c \
+ openbox/menuframe.h \
+ openbox/menu.c \
+ openbox/menu.h \
+ openbox/misc.h \
+ openbox/mouse.c \
+ openbox/mouse.h \
+ openbox/moveresize.c \
+ openbox/moveresize.h \
+ openbox/mwm.h \
+ openbox/openbox.c \
+ openbox/openbox.h \
+ openbox/ping.c \
+ openbox/ping.h \
+ openbox/place.c \
+ openbox/place.h \
+ openbox/prompt.c \
+ openbox/prompt.h \
+ openbox/popup.c \
+ openbox/popup.h \
+ openbox/resist.c \
+ openbox/resist.h \
+ openbox/screen.c \
+ openbox/screen.h \
+ openbox/session.c \
+ openbox/session.h \
+ openbox/stacking.c \
+ openbox/stacking.h \
+ openbox/startupnotify.c \
+ openbox/startupnotify.h \
+ openbox/translate.c \
+ openbox/translate.h \
+ openbox/window.c \
+ openbox/window.h
+
+
+## gnome-panel-control ##
+
+tools_gnome_panel_control_gnome_panel_control_CPPFLAGS = \
+ $(X_CFLAGS)
+tools_gnome_panel_control_gnome_panel_control_LDADD = \
+ $(X_LIBS)
+tools_gnome_panel_control_gnome_panel_control_SOURCES = \
+ tools/gnome-panel-control/gnome-panel-control.c
+
+## obxprop ##
+
+tools_obxprop_obxprop_CPPFLAGS = \
+ $(GLIB_CFLAGS) \
+ $(X_CFLAGS)
+tools_obxprop_obxprop_LDADD = \
+ $(GLIB_LIBS) \
+ $(X_LIBS)
+tools_obxprop_obxprop_SOURCES = \
+ tools/obxprop/obxprop.c
+
+## gdm-control ##
+
+tools_gdm_control_gdm_control_CPPFLAGS = \
+ $(XAUTH_CFLAGS) \
+ $(X_CFLAGS) \
+ $(GLIB_CFLAGS)
+tools_gdm_control_gdm_control_LDADD = \
+ $(XAUTH_LIBS) \
+ $(X_LIBS) \
+ $(GLIB_LIBS)
+tools_gdm_control_gdm_control_SOURCES = \
+ tools/gdm-control/gdm-control.c
+
+
+## default button masks ##
+dist_docxbm_DATA = \
+ data/xbm/bullet.xbm \
+ data/xbm/close.xbm \
+ data/xbm/desk_toggled.xbm \
+ data/xbm/desk.xbm \
+ data/xbm/iconify.xbm \
+ data/xbm/max_toggled.xbm \
+ data/xbm/max.xbm \
+ data/xbm/shade_toggled.xbm \
+ data/xbm/shade.xbm
+
+
+## themes ##
+
+clearlooks_themedir = $(themedir)/Clearlooks/openbox-3
+dist_clearlooks_theme_DATA= \
+ themes/Clearlooks/openbox-3/themerc
+
+clearlooks34_themedir = $(themedir)/Clearlooks-3.4/openbox-3
+dist_clearlooks34_theme_DATA= \
+ themes/Clearlooks-3.4/openbox-3/themerc
+
+clearlooksolive_themedir = $(themedir)/Clearlooks-Olive/openbox-3
+dist_clearlooksolive_theme_DATA= \
+ themes/Clearlooks-Olive/openbox-3/themerc
+
+mikachu_themedir = $(themedir)/Mikachu/openbox-3
+dist_mikachu_theme_DATA= \
+ themes/Mikachu/openbox-3/bullet.xbm \
+ themes/Mikachu/openbox-3/close.xbm \
+ themes/Mikachu/openbox-3/desk.xbm \
+ themes/Mikachu/openbox-3/iconify.xbm \
+ themes/Mikachu/openbox-3/max.xbm \
+ themes/Mikachu/openbox-3/themerc
+
+
+natura_themedir = $(themedir)/Natura/openbox-3
+dist_natura_theme_DATA= \
+ themes/Natura/openbox-3/close_hover.xbm \
+ themes/Natura/openbox-3/close.xbm \
+ themes/Natura/openbox-3/desk_toggled.xbm \
+ themes/Natura/openbox-3/desk_hover.xbm \
+ themes/Natura/openbox-3/desk.xbm \
+ themes/Natura/openbox-3/iconify_hover.xbm \
+ themes/Natura/openbox-3/iconify.xbm \
+ themes/Natura/openbox-3/max_hover.xbm \
+ themes/Natura/openbox-3/max_toggled.xbm \
+ themes/Natura/openbox-3/max.xbm \
+ themes/Natura/openbox-3/shade.xbm \
+ themes/Natura/openbox-3/shade_hover.xbm \
+ themes/Natura/openbox-3/themerc
+
+artwizboxed_themedir = $(themedir)/Artwiz-boxed/openbox-3
+dist_artwizboxed_theme_DATA= \
+ themes/Artwiz-boxed/openbox-3/themerc
+
+bear2_themedir = $(themedir)/Bear2/openbox-3
+dist_bear2_theme_DATA= \
+ themes/Bear2/openbox-3/close_pressed.xbm \
+ themes/Bear2/openbox-3/close.xbm \
+ themes/Bear2/openbox-3/desk_toggled.xbm \
+ themes/Bear2/openbox-3/desk.xbm \
+ themes/Bear2/openbox-3/iconify_pressed.xbm \
+ themes/Bear2/openbox-3/iconify.xbm \
+ themes/Bear2/openbox-3/max_pressed.xbm \
+ themes/Bear2/openbox-3/max_toggled.xbm \
+ themes/Bear2/openbox-3/max.xbm \
+ themes/Bear2/openbox-3/shade_pressed.xbm \
+ themes/Bear2/openbox-3/shade.xbm \
+ themes/Bear2/openbox-3/themerc
+
+orang_themedir = $(themedir)/Orang/openbox-3
+dist_orang_theme_DATA= \
+ themes/Orang/openbox-3/themerc
+
+onyx_themedir = $(themedir)/Onyx/openbox-3
+dist_onyx_theme_DATA= \
+ themes/Onyx/openbox-3/themerc
+
+onyxcitrus_themedir = $(themedir)/Onyx-Citrus/openbox-3
+dist_onyxcitrus_theme_DATA= \
+ themes/Onyx-Citrus/openbox-3/themerc
+
+syscrash_themedir = $(themedir)/Syscrash/openbox-3
+dist_syscrash_theme_DATA= \
+ themes/Syscrash/openbox-3/max_disabled.xbm \
+ themes/Syscrash/openbox-3/max_pressed.xbm \
+ themes/Syscrash/openbox-3/max_toggled.xbm \
+ themes/Syscrash/openbox-3/max.xbm \
+ themes/Syscrash/openbox-3/themerc
+
+## public headers ##
+
+rrpubinclude_HEADERS = \
+ obrender/color.h \
+ obrender/font.h \
+ obrender/geom.h \
+ obrender/gradient.h \
+ obrender/image.h \
+ obrender/instance.h \
+ obrender/mask.h \
+ obrender/render.h \
+ obrender/theme.h \
+ obrender/version.h
+
+obtpubinclude_HEADERS = \
+ obt/link.h \
+ obt/display.h \
+ obt/keyboard.h \
+ obt/xml.h \
+ obt/paths.h \
+ obt/prop.h \
+ obt/signal.h \
+ obt/util.h \
+ obt/version.h \
+ obt/xqueue.h
+
+nodist_pkgconfig_DATA = \
+ obrender/obrender-3.5.pc \
+ obt/obt-3.5.pc
+
+## data ##
+
+dist_apps_DATA = \
+ data/openbox.desktop
+
+dist_pixmap_DATA = \
+ data/openbox.png
+
+dist_rc_DATA = \
+ data/rc.xml \
+ data/menu.xml
+
+edit = $(SED) \
+ -e 's!@version\@!$(VERSION)!' \
+ -e 's!@configdir\@!$(configdir)!' \
+ -e 's!@rcdir\@!$(rcdir)!' \
+ -e 's!@libexecdir\@!$(libexecdir)!' \
+ -e 's!@bindir\@!$(bindir)!'
+
+data/autostart/autostart: $(top_srcdir)/data/autostart/autostart.in Makefile
+ @echo make: creating $@
+ @test -d $(shell dirname $(top_builddir)/$@) || \
+ mkdir $(shell dirname $(top_builddir)/$@)
+ @$(edit) $< >$(top_builddir)/$@
+
+data/autostart/openbox-autostart: $(top_srcdir)/data/autostart/openbox-autostart.in Makefile
+ @echo make: creating $@
+ @test -d $(shell dirname $(top_builddir)/$@) || \
+ mkdir $(shell dirname $(top_builddir)/$@)
+ @$(edit) $< >$(top_builddir)/$@
+
+%.desktop: %.desktop.in Makefile
+ @echo make: creating $@
+ @$(edit) $< >$@
+
+%-session: %-session.in Makefile
+ @echo make: creating $@
+ @$(edit) $< >$@
+
+%.1.in: %.1.sgml
+ @echo make: creating $@
+ @docbook-to-man $< >$@
+
+%.1: %.1.in Makefile
+ @echo make: creating $@
+ @$(edit) $< >$@
+
+dist_gnomewmfiles_DATA = \
+ data/gnome-wm-properties/openbox.desktop
+
+nodist_xsessions_DATA = \
+ data/xsession/openbox.desktop \
+ data/xsession/openbox-gnome.desktop \
+ data/xsession/openbox-kde.desktop
+
+dist_gnomesession_DATA = \
+ data/gnome-session/openbox-gnome.session \
+ data/gnome-session/openbox-gnome-fallback.session
+
+dist_noinst_DATA = \
+ data/rc.xsd \
+ data/menu.xsd \
+ data/autostart/autostart.in \
+ data/autostart/openbox-autostart.in \
+ data/xsession/openbox.desktop.in \
+ data/xsession/openbox-gnome.desktop.in \
+ data/xsession/openbox-kde.desktop.in \
+ data/xsession/openbox-session.in \
+ data/xsession/openbox-gnome-session.in \
+ data/xsession/openbox-kde-session.in \
+ doc/openbox.1.sgml \
+ doc/openbox.1.in \
+ doc/openbox-session.1.sgml \
+ doc/openbox-session.1.in \
+ doc/openbox-gnome-session.1.sgml \
+ doc/openbox-gnome-session.1.in \
+ doc/openbox-kde-session.1.sgml \
+ doc/openbox-kde-session.1.in \
+ doc/obxprop.1.sgml \
+ doc/obxprop.1.in \
+ obrender/version.h.in \
+ obrender/obrender-3.5.pc.in \
+ obt/obt-3.5.pc.in \
+ obt/version.h.in \
+ tools/themeupdate/themeupdate.py \
+ tests/hideshow.py \
+ tests/Makefile \
+ tests/aspect.c \
+ tests/fullscreen.c \
+ tests/grav.c \
+ tests/grouptran.c \
+ tests/icons.c \
+ tests/modal2.c \
+ tests/modal3.c \
+ tests/modal.c \
+ tests/noresize.c \
+ tests/override.c \
+ tests/positioned.c \
+ tests/strut.c \
+ tests/title.c \
+ tests/urgent.c
+
+dist_doc_DATA = \
+ COMPLIANCE \
+ README \
+ AUTHORS \
+ CHANGELOG \
+ COPYING \
+ data/rc.xsd \
+ data/menu.xsd \
+ doc/rc-mouse-focus.xml
+
+nodist_man_MANS = \
+ doc/openbox.1 \
+ doc/openbox-session.1 \
+ doc/openbox-gnome-session.1 \
+ doc/openbox-kde-session.1 \
+ doc/obxprop.1
+
+EXTRA_DIST = \
+ config.rpath
+
+# make clean doesn't delete these for some reason, even though they are
+# built by make
+CLEANFILES = \
+ $(nodist_man_MANS) \
+ $(nodist_bin_SCRIPTS) \
+ $(nodist_xsessions_DATA) \
+ $(nodist_rc_SCRIPTS) \
+ $(nodist_libexec_SCRIPTS)
+
+#doc:
+# $(MAKE) -$(MAKEFLAGS) -C doc/doxygen doc
+
+distclean-local:
+ for d in . m4 po obrender parser obt openbox; do \
+ for p in core core.* gmon.out *\~ *.orig *.rej .\#*; do \
+ rm -f "$$d/$$p"; \
+ done \
+ done
+
+.PHONY: doc
diff --git a/README b/README
new file mode 100644
index 0000000..9c66864
--- /dev/null
+++ b/README
@@ -0,0 +1,23 @@
+Openbox
+Copyright (C) 2004-2007 Mikael Magnusson
+Copyright (C) 2002-2007 Dana Jansens
+
+----
+
+ This software is OSI Certified Open Source Software.
+ OSI Certified is a certification mark of the Open Source Initiative.
+
+----
+
+ 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.
+
diff --git a/README.GIT b/README.GIT
new file mode 100644
index 0000000..a6ad99b
--- /dev/null
+++ b/README.GIT
@@ -0,0 +1,61 @@
+To build Openbox from git you need:
+
+A C Compiler (GNU GCC 3.2+ suggested)
+-GNU Gettext 0.14.4
+-GNU Autoconf 2.50+
+-GNU Automake 1.11+
+-GNU Libtool
+-Xlib library/headers (devel package)
+-Pkg-Config
+-Glib 2.0+ library/headers (devel package) (http://www.gtk.org)
+-libxml2 2.0+ library/headers (devel package)
+-Pango 1.10+ library/headers (devel package)
+-cvs or git or neither, depending on how gettext was installed*
+
+Also you will probably want:
+-Imlib2 library/headers (devel package)
+-X Cursor library/headers (devel package)
+-Startup Notification library/headers 0.8+ (devel package)
+
+We recommend the latest versions of all these packages.
+
+*) for versions below 0.18.1 cvs is needed, above that, see autopoint --version
+
+Do the following to build and install Openbox in git:
+
+% ./bootstrap
+% ./configure
+% make
+su to root and
+% make install
+or
+% sudo make install
+
+Don't try running it from the openbox/ directory without installing, it won't
+work. It needs to be installed before it is run.
+
+The following commands will be available: openbox-session,
+ openbox-gnome-session, openbox-kde-session, and openbox.
+
+See the man pages for details about them. If you want to run Openbox on its
+ own, you probably want to run "openbox-session".
+
+
+
+----
+In Ubuntu, you want these packages:
+
+gcc
+gettext
+automake
+autoconf
+libtool
+libpango1.0-dev
+pkg-config
+libglib2.0-dev
+libxml2-dev
+libstartup-notification0-dev
+xorg-dev
+libimlib2-dev
+
+----
diff --git a/README.NLS b/README.NLS
new file mode 100644
index 0000000..7495afd
--- /dev/null
+++ b/README.NLS
@@ -0,0 +1,3 @@
+Use this command to create a new .po
+
+msginit --locale xx_YY file.pot
diff --git a/bootstrap b/bootstrap
new file mode 100755
index 0000000..6a5b10f
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,18 @@
+#! /bin/sh
+
+sh() {
+ /bin/sh -c "set -x; $*"
+}
+
+export WANT_AUTOMAKE=1.9
+
+sh autopoint --force || exit 1 # for GNU gettext
+sh libtoolize --copy --force --automake || exit 1
+sh aclocal -I m4 $ACLOCAL_FLAGS || exit 1
+#sh autoheader || exit 1
+sh autoconf || exit 1
+sh automake --include-deps --add-missing --copy || exit 1
+
+echo
+echo You are now ready to run ./configure
+echo enjoy!
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..a183309
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,238 @@
+AC_PREREQ([2.54])
+AC_INIT([openbox], [3.5.0], [http://bugzilla.icculus.org])
+AC_CONFIG_SRCDIR([openbox/openbox.c])
+
+AM_INIT_AUTOMAKE([foreign])
+AM_SILENT_RULES([yes])
+
+OB_VERSION=$PACKAGE_VERSION
+AC_SUBST(OB_VERSION)
+
+dnl Making releases:
+dnl RR_MICRO_VERSION += 1;
+dnl RR_INTERFACE_AGE += 1;
+dnl R_BINARY_AGE += 1;
+dnl if any functions have been added, set RR_INTERFACE_AGE to 0.
+dnl if backwards compatibility has been broken,
+dnl set RR_BINARY_AGE and RR_INTERFACE_AGE to 0.
+dnl
+dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match
+dnl
+RR_MAJOR_VERSION=3
+RR_MINOR_VERSION=5
+RR_MICRO_VERSION=28
+RR_INTERFACE_AGE=1
+RR_BINARY_AGE=1
+RR_VERSION=$RR_MAJOR_VERSION.$RR_MINOR_VERSION
+
+OBT_MAJOR_VERSION=3
+OBT_MINOR_VERSION=5
+OBT_MICRO_VERSION=1
+OBT_INTERFACE_AGE=1
+OBT_BINARY_AGE=1
+OBT_VERSION=$OBT_MAJOR_VERSION.$OBT_MINOR_VERSION
+
+AC_SUBST(RR_MAJOR_VERSION)
+AC_SUBST(RR_MINOR_VERSION)
+AC_SUBST(RR_MICRO_VERSION)
+AC_SUBST(RR_INTERFACE_AGE)
+AC_SUBST(RR_BINARY_AGE)
+AC_SUBST(RR_VERSION)
+AC_SUBST(OBT_MAJOR_VERSION)
+AC_SUBST(OBT_MINOR_VERSION)
+AC_SUBST(OBT_MICRO_VERSION)
+AC_SUBST(OBT_INTERFACE_AGE)
+AC_SUBST(OBT_BINARY_AGE)
+AC_SUBST(OBT_VERSION)
+
+dnl Libtool versioning
+RR_RELEASE=$RR_MAJOR_VERSION.$RR_MINOR_VERSION
+RR_CURRENT=`expr $RR_MICRO_VERSION - $RR_INTERFACE_AGE`
+RR_REVISION=$RR_INTERFACE_AGE
+RR_AGE=`expr $RR_BINARY_AGE - $RR_INTERFACE_AGE`
+RR_CURRENT_MINUS_AGE=`expr $RR_CURRENT - $RR_AGE`
+
+OBT_RELEASE=$OBT_MAJOR_VERSION.$OBT_MINOR_VERSION
+OBT_CURRENT=`expr $OBT_MICRO_VERSION - $OBT_INTERFACE_AGE`
+OBT_REVISION=$OBT_INTERFACE_AGE
+OBT_AGE=`expr $OBT_BINARY_AGE - $OBT_INTERFACE_AGE`
+OBT_CURRENT_MINUS_AGE=`expr $OBT_CURRENT - $OBT_AGE`
+
+AC_SUBST(RR_RELEASE)
+AC_SUBST(RR_CURRENT)
+AC_SUBST(RR_REVISION)
+AC_SUBST(RR_AGE)
+AC_SUBST(RR_CURRENT_MINUS_AGE)
+AC_SUBST(OBT_RELEASE)
+AC_SUBST(OBT_CURRENT)
+AC_SUBST(OBT_REVISION)
+AC_SUBST(OBT_AGE)
+AC_SUBST(OBT_CURRENT_MINUS_AGE)
+
+AC_PREFIX_DEFAULT([/usr/local])
+test "$prefix" = "NONE" && prefix=$ac_default_prefix
+
+dnl Determine build target
+OB_DEBUG
+dnl Pick compiler specific/build target flags, and set $CVS
+AM_PROG_CC_C_O
+OB_COMPILER_FLAGS
+AC_C_CONST
+AC_C_INLINE
+
+AC_PROG_LIBTOOL
+AC_SUBST(LIBTOOL_DEPS)
+LIBTOOL="$LIBTOOL --silent"
+
+AC_PROG_INSTALL
+
+AM_GNU_GETTEXT_VERSION(0.15)
+AM_GNU_GETTEXT([external])
+
+AC_CHECK_HEADERS(ctype.h dirent.h errno.h fcntl.h grp.h locale.h pwd.h)
+AC_CHECK_HEADERS(signal.h string.h stdio.h stdlib.h unistd.h sys/stat.h)
+AC_CHECK_HEADERS(sys/select.h sys/socket.h sys/time.h sys/types.h sys/wait.h)
+
+AC_PATH_PROG([SED], [sed], [no])
+if test "$SED" = "no"; then
+ AC_MSG_ERROR([The program "sed" is not available. This program is required to build Openbox.])
+fi
+
+AC_PATH_PROG([DIRNAME], [dirname], [no])
+if test "$DIRNAME" = "no"; then
+ AC_MSG_ERROR([The program "dirname" is not available. This program is required to build Openbox.])
+fi
+
+PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.14.0])
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
+
+PKG_CHECK_MODULES(PANGO, [pango >= 1.8.0 pangoxft >= 1.8.0])
+AC_SUBST(PANGO_CFLAGS)
+AC_SUBST(PANGO_LIBS)
+
+PKG_CHECK_MODULES(XML, [libxml-2.0 >= 2.6.0])
+AC_SUBST(XML_CFLAGS)
+AC_SUBST(XML_LIBS)
+
+AC_ARG_ENABLE(startup-notification,
+ AC_HELP_STRING(
+ [--disable-startup-notification],
+ [disable the startup notification library. [default=enabled]]
+ ),
+ [enable_sn=$enableval],
+ [enable_sn=yes]
+)
+
+if test "$enable_sn" = yes; then
+PKG_CHECK_MODULES(LIBSN, [libstartup-notification-1.0 >= 0.8],
+ [
+ AC_DEFINE(USE_LIBSN, [1], [Use startup-notification])
+ AC_SUBST(LIBSN_CFLAGS)
+ AC_SUBST(LIBSN_LIBS)
+ sn_found=yes
+ ],
+ [
+ sn_found=no
+ ]
+)
+else
+ sn_found=no
+fi
+
+AC_ARG_ENABLE(xcursor,
+ AC_HELP_STRING(
+ [--disable-xcursor],
+ [disable use of the X Cursor library. [default=enabled]]
+ ),
+ [enable_xcursor=$enableval],
+ [enable_xcursor=yes]
+)
+
+if test "$enable_xcursor" = yes; then
+PKG_CHECK_MODULES(XCURSOR, [xcursor],
+ [
+ AC_DEFINE(USE_XCURSOR, [1], [Use X Cursor library])
+ AC_SUBST(XCURSOR_CFLAGS)
+ AC_SUBST(XCURSOR_LIBS)
+ xcursor_found=yes
+ ],
+ [
+ xcursor_found=no
+ ]
+)
+else
+ xcursor_found=no
+fi
+
+AC_ARG_ENABLE(imlib2,
+ AC_HELP_STRING(
+ [--disable-imlib2],
+ [disable use of Imlib2 image library for loading icons. [default=enabled]]
+ ),
+ [enable_imlib2=$enableval],
+ [enable_imlib2=yes]
+)
+
+if test "$enable_imlib2" = yes; then
+PKG_CHECK_MODULES(IMLIB2, [imlib2],
+ [
+ AC_DEFINE(USE_IMLIB2, [1], [Use Imlib2 image library])
+ AC_SUBST(IMLIB2_CFLAGS)
+ AC_SUBST(IMLIB2_LIBS)
+ # export it for the pkg-config file
+ PKG_CONFIG_IMLIB=imlib2
+ AC_SUBST(PKG_CONFIG_IMLIB)
+ imlib2_found=yes
+ ],
+ [
+ imlib2_found=no
+ ]
+)
+else
+ imlib2_found=no
+fi
+
+AM_CONDITIONAL(USE_IMLIB2, [test $imlib2_found = yes])
+
+dnl Check for session management
+X11_SM
+
+#EFENCE_LIBS=-lefence
+EFENCE_LIBS=""
+AC_SUBST(EFENCE_LIBS)
+
+dnl Check for X11 extensions
+X11_EXT_XKB
+X11_EXT_XRANDR
+X11_EXT_SHAPE
+X11_EXT_XINERAMA
+X11_EXT_SYNC
+X11_EXT_AUTH
+
+AC_CONFIG_FILES([
+ Makefile
+ m4/Makefile
+ po/Makefile.in
+ obrender/obrender-3.5.pc
+ obt/obt-3.5.pc
+ obrender/version.h
+ obt/version.h
+ version.h
+])
+AC_CONFIG_COMMANDS([doc],
+ [test -d doc || mkdir doc])
+AC_CONFIG_COMMANDS([data],
+ [test -d data || mkdir data])
+AC_CONFIG_COMMANDS([data/xsession],
+ [test -d data/xsession || mkdir data/xsession])
+AC_OUTPUT
+
+AC_MSG_RESULT
+AC_MSG_RESULT([Compiling with these options:
+ Startup Notification... $sn_found
+ X Cursor Library... $xcursor_found
+ Session Management... $SM
+ Imlib2 library... $imlib2_found
+ ])
+AC_MSG_RESULT([configure complete, now type "make"])
diff --git a/data/Makefile b/data/Makefile
new file mode 100644
index 0000000..b90edac
--- /dev/null
+++ b/data/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/data/autostart/Makefile b/data/autostart/Makefile
new file mode 100644
index 0000000..b90edac
--- /dev/null
+++ b/data/autostart/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/data/autostart/autostart.in b/data/autostart/autostart.in
new file mode 100644
index 0000000..1c261a0
--- /dev/null
+++ b/data/autostart/autostart.in
@@ -0,0 +1,17 @@
+#
+# These things are run when an Openbox X Session is started.
+# You may place a similar script in $HOME/.config/openbox/autostart
+# to run user-specific things.
+#
+
+# If you want to use GNOME config tools...
+#
+#if test -x @libexecdir@/gnome-settings-daemon >/dev/null; then
+# @libexecdir@/gnome-settings-daemon &
+#elif which gnome-settings-daemon >/dev/null; then
+# gnome-settings-daemon &
+#fi
+
+# If you want to use XFCE config tools...
+#
+#xfce-mcs-manager &
diff --git a/data/autostart/openbox-autostart.in b/data/autostart/openbox-autostart.in
new file mode 100755
index 0000000..063c635
--- /dev/null
+++ b/data/autostart/openbox-autostart.in
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# Set a background color
+BG=""
+if which hsetroot >/dev/null; then
+ BG=hsetroot
+elif which esetroot >/dev/null; then
+ BG=esetroot
+elif which xsetroot >/dev/null; then
+ BG=xsetroot
+fi
+test -z $BG || $BG -solid "#303030"
+
+GLOBALAUTOSTART="@rcdir@/autostart"
+AUTOSTART="${XDG_CONFIG_HOME:-"$HOME/.config"}/openbox/autostart"
+
+# Run the global openbox autostart script
+if test -f $GLOBALAUTOSTART; then
+ sh $GLOBALAUTOSTART
+elif test -f $GLOBALAUTOSTART.sh; then
+ sh $GLOBALAUTOSTART.sh
+fi
+
+# Run the user openbox autostart script
+if test -f $AUTOSTART; then
+ sh $AUTOSTART
+elif test -f $AUTOSTART.sh; then
+ sh $AUTOSTART.sh
+fi
+
+# Run the XDG autostart stuff. These are found in /etc/xdg/autostart and
+# in $HOME/.config/autostart. This requires PyXDG to be installed.
+# See openbox-xdg-autostart --help for more details.
+@libexecdir@/openbox-xdg-autostart "$@"
diff --git a/data/autostart/openbox-xdg-autostart b/data/autostart/openbox-xdg-autostart
new file mode 100755
index 0000000..ea76028
--- /dev/null
+++ b/data/autostart/openbox-xdg-autostart
@@ -0,0 +1,198 @@
+#!/usr/bin/env python
+
+# openbox-xdg-autostart runs things based on the XDG autostart specification
+# Copyright (C) 2008 Dana Jansens
+#
+# XDG autostart specification can be found here:
+# http://standards.freedesktop.org/autostart-spec/
+#
+#
+#
+# LICENSE:
+# 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.
+
+ME="openbox-xdg-autostart"
+VERSION="1.1"
+
+import os, glob, sys
+try:
+ from xdg import BaseDirectory
+ from xdg.DesktopEntry import DesktopEntry
+ from xdg.Exceptions import ParsingError
+except ImportError:
+ print
+ print "ERROR:", ME, "requires PyXDG to be installed"
+ print
+ sys.exit(1)
+
+def main(argv=sys.argv):
+ if "--help" in argv[1:]:
+ show_help()
+ return 0
+ if "--version" in argv[1:]:
+ show_version()
+ return 0
+
+ # get the autostart directories
+ autodirs = BaseDirectory.load_config_paths("autostart")
+
+ # find all the autostart files
+ files = []
+ for dir in autodirs:
+ for path in glob.glob(os.path.join(dir, '*.desktop')):
+ try:
+ autofile = AutostartFile(path)
+ except ParsingError:
+ print "Invalid .desktop file: " + path
+ else:
+ if not autofile in files:
+ files.append(autofile)
+
+ list = False
+ if "--list" in argv[1:]:
+ list = True
+ argv.remove("--list")
+
+ # run them !
+ environments = argv[1:]
+ for autofile in files:
+ if list: autofile.display(environments)
+ else: autofile.run(environments)
+
+class AutostartFile:
+ def __init__(self, path):
+ self.path = path
+ self.filename = os.path.basename(path)
+ self.dirname = os.path.dirname(path)
+ self.de = DesktopEntry(path)
+
+ def __eq__(self, other):
+ return self.filename == other.filename
+
+ def __str__(self):
+ return self.path + " : " + self.de.getName()
+
+ def _isexecfile(self, path):
+ return os.access(path, os.X_OK)
+
+ def _findFile(self, path, search, match_func):
+ # check empty path
+ if not path: return None
+ # check absolute path
+ if path[0] == '/':
+ if match_func(path): return path
+ else: return None
+ else:
+ # check relative path
+ for dirname in search.split(os.pathsep):
+ if dirname != "":
+ candidate = os.path.join(dirname, path)
+ if (match_func(candidate)): return candidate
+
+ def _alert(self, str, info=False):
+ if info:
+ print "\t ", str
+ else:
+ print "\t*", str
+
+ def _showInEnvironment(self, envs, verbose=False):
+ default = not self.de.getOnlyShowIn()
+ noshow = False
+ force = False
+ for i in self.de.getOnlyShowIn():
+ if i in envs: force = True
+ for i in self.de.getNotShowIn():
+ if i in envs: noshow = True
+
+ if verbose:
+ if not default and not force:
+ s = ""
+ for i in self.de.getOnlyShowIn():
+ if s: s += ", "
+ s += i
+ self._alert("Excluded by: OnlyShowIn (" + s + ")")
+ if default and noshow and not force:
+ s = ""
+ for i in self.de.getNotShowIn():
+ if s: s += ", "
+ s += i
+ self._alert("Excluded by: NotShowIn (" + s + ")")
+ return (default and not noshow) or force
+
+ def _shouldRun(self, envs, verbose=False):
+ if not self.de.getExec():
+ if verbose: self._alert("Excluded by: Missing Exec field")
+ return False
+ if self.de.getHidden():
+ if verbose: self._alert("Excluded by: Hidden")
+ return False
+ if self.de.getTryExec():
+ if not self._findFile(self.de.getTryExec(), os.getenv("PATH"),
+ self._isexecfile):
+ if verbose: self._alert("Excluded by: TryExec (" +
+ self.de.getTryExec() + ")")
+ return False
+ if not self._showInEnvironment(envs, verbose):
+ return False
+ return True
+
+ def display(self, envs):
+ if self._shouldRun(envs):
+ print "[*] " + self.de.getName()
+ else:
+ print "[ ] " + self.de.getName()
+ self._alert("File: " + self.path, info=True)
+ if self.de.getExec():
+ self._alert("Executes: " + self.de.getExec(), info=True)
+ self._shouldRun(envs, True)
+ print
+
+ def run(self, envs):
+ here = os.getcwd()
+ if self.de.getPath():
+ os.chdir(self.de.getPath())
+ if self._shouldRun(envs):
+ args = ["/bin/sh", "-c", "exec " + self.de.getExec()]
+ os.spawnv(os.P_NOWAIT, args[0], args);
+ os.chdir(here)
+
+def show_help():
+ print "Usage:", ME, "[OPTION]... [ENVIRONMENT]..."
+ print
+ print "This tool will run xdg autostart .desktop files"
+ print
+ print "OPTIONS"
+ print " --list Show a list of the files which would be run"
+ print " Files which would be run are marked with an asterix"
+ print " symbol [*]. For files which would not be run,"
+ print " information is given for why they are excluded"
+ print " --help Show this help and exit"
+ print " --version Show version and copyright information"
+ print
+ print "ENVIRONMENT specifies a list of environments for which to run autostart"
+ print "applications. If none are specified, only applications which do not "
+ print "limit themselves to certain environments will be run."
+ print
+ print "ENVIRONMENT can be one or more of:"
+ print " GNOME Gnome Desktop"
+ print " KDE KDE Desktop"
+ print " ROX ROX Desktop"
+ print " XFCE XFCE Desktop"
+ print " Old Legacy systems"
+ print
+
+def show_version():
+ print ME, VERSION
+ print "Copyright (c) 2008 Dana Jansens"
+ print
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/data/environment b/data/environment
new file mode 100644
index 0000000..3311bd6
--- /dev/null
+++ b/data/environment
@@ -0,0 +1,10 @@
+#
+# Set system-wide environment variables here for Openbox
+# User-specific variables should be placed in $HOME/.config/openbox/environment
+#
+
+# To set your language for displaying messages and time/date formats, use the following:
+#LANG=en_CA.UTF8
+
+# To set your keyboard layout, you need to modify your X config:
+# http://www.google.com/search?q=how+to+set+keyboard+layout+xorg
diff --git a/data/gnome-session/Makefile b/data/gnome-session/Makefile
new file mode 100644
index 0000000..b90edac
--- /dev/null
+++ b/data/gnome-session/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/data/gnome-session/openbox-gnome-fallback.session b/data/gnome-session/openbox-gnome-fallback.session
new file mode 100644
index 0000000..156f2c3
--- /dev/null
+++ b/data/gnome-session/openbox-gnome-fallback.session
@@ -0,0 +1,6 @@
+[GNOME Session]
+Name=GNOME/Openbox fallback (Safe Mode)
+RequiredComponents=gnome-settings-daemon;
+RequiredProviders=windowmanager;
+DefaultProvider-windowmanager=openbox
+DesktopName=GNOME
diff --git a/data/gnome-session/openbox-gnome.session b/data/gnome-session/openbox-gnome.session
new file mode 100644
index 0000000..3399c2c
--- /dev/null
+++ b/data/gnome-session/openbox-gnome.session
@@ -0,0 +1,9 @@
+[GNOME Session]
+Name=GNOME/Openbox
+RequiredComponents=gnome-settings-daemon;
+# Try load with the gnome-panel and use the fallback if we can't load a panel
+RequiredProviders=windowmanager;panel
+DefaultProvider-windowmanager=openbox
+DefaultProvider-panel=gnome-panel
+FallbackSession=openbox-gnome-fallback
+DesktopName=GNOME
diff --git a/data/gnome-wm-properties/Makefile b/data/gnome-wm-properties/Makefile
new file mode 100644
index 0000000..b90edac
--- /dev/null
+++ b/data/gnome-wm-properties/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/data/gnome-wm-properties/openbox.desktop b/data/gnome-wm-properties/openbox.desktop
new file mode 100644
index 0000000..67e49e4
--- /dev/null
+++ b/data/gnome-wm-properties/openbox.desktop
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Type=Application
+Name=Openbox
+Exec=openbox
+
+# name we put on the WM spec check window
+X-GNOME-WMName=Openbox
+
+# our config tool
+ConfigExec=obconf
+
+[Window Manager]
+SessionManaged=true
diff --git a/data/menu.xml b/data/menu.xml
new file mode 100644
index 0000000..39da04d
--- /dev/null
+++ b/data/menu.xml
@@ -0,0 +1,394 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<openbox_menu xmlns="http://openbox.org/3.4/menu">
+
+<menu id="apps-accessories-menu" label="Accessories">
+ <item label="Calculator">
+ <action name="Execute">
+ <command>gnome-calculator</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Character Map">
+ <action name="Execute">
+ <command>gnome-character-map</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Ark File Archiver">
+ <action name="Execute">
+ <command>ark</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+</menu>
+
+<menu id="apps-editors-menu" label="Editors">
+ <item label="GVim">
+ <action name="Execute">
+ <command>gvim</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ <wmclass>GVim</wmclass>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Emacs">
+ <action name="Execute">
+ <command>emacs</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ <wmclass>Emacs</wmclass>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="GEdit">
+ <action name="Execute">
+ <command>gedit</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Kate">
+ <action name="Execute">
+ <command>kate</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Kwrite">
+ <action name="Execute">
+ <command>kwrite</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+</menu>
+
+<menu id="apps-term-menu" label="Terminals">
+ <item label="Rxvt Unicode">
+ <action name="Execute">
+ <command>urxvt</command>
+ </action>
+ </item>
+ <item label="Gnome Terminal">
+ <action name="Execute">
+ <command>gnome-terminal</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Xfce Terminal">
+ <action name="Execute">
+ <command>xfce4-terminal</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Konsole">
+ <action name="Execute">
+ <command>konsole</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Xterm">
+ <action name="Execute"><command>xterm</command></action>
+ </item>
+</menu>
+
+<menu id="apps-net-menu" label="Internet">
+ <item label="Firefox">
+ <action name="Execute">
+ <command>firefox</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ <wmclass>Firefox</wmclass>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Opera">
+ <action name="Execute">
+ <command>opera</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ <wmclass>Opera</wmclass>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Konqueror">
+ <action name="Execute">
+ <command>konqueror</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Epiphany">
+ <action name="Execute">
+ <command>epiphany</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Pidgin Instant Messenger">
+ <action name="Execute">
+ <command>pidgin</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Kopete Instant Messenger">
+ <action name="Execute">
+ <command>kopete</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="XChat">
+ <action name="Execute">
+ <command>xchat</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+</menu>
+
+<menu id="apps-office-menu" label="Office">
+ <item label="OpenOffice Base">
+ <action name="Execute">
+ <command>ooffice -base</command>
+ </action>
+ </item>
+ <item label="OpenOffice Calc">
+ <action name="Execute">
+ <command>ooffice -calc</command>
+ </action>
+ </item>
+ <item label="OpenOffice Draw">
+ <action name="Execute">
+ <command>ooffice -draw</command>
+ </action>
+ </item>
+ <item label="OpenOffice Impress">
+ <action name="Execute">
+ <command>ooffice -impress</command>
+ </action>
+ </item>
+ <item label="OpenOffice Math">
+ <action name="Execute">
+ <command>ooffice -math</command>
+ </action>
+ </item>
+ <item label="OpenOffice Printer Administration">
+ <action name="Execute">
+ <command>ooffice-printeradmin</command>
+ </action>
+ </item>
+ <item label="OpenOffice Writer">
+ <action name="Execute">
+ <command>ooffice -writer</command>
+ </action>
+ </item>
+</menu>
+
+<menu id="apps-multimedia-menu" label="Multimedia">
+ <item label="Amarok">
+ <action name="Execute">
+ <command>amarok</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Rhythmbox">
+ <action name="Execute">
+ <command>rhythmbox</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="K3b">
+ <action name="Execute">
+ <command>k3b</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="MPlayer">
+ <action name="Execute">
+ <command>gmplayer</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ <wmclass>MPlayer</wmclass>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Totem">
+ <action name="Execute">
+ <command>totem</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+</menu>
+
+<menu id="apps-fileman-menu" label="File Managers">
+ <item label="Nautilus">
+ <action name="Execute">
+ <command>nautilus --no-desktop --browser</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Thunar">
+ <action name="Execute">
+ <command>Thunar</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="KDE File Manager">
+ <action name="Execute">
+ <command>kfmclient openURL ~</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Rox">
+ <action name="Execute">
+ <command>rox</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ <wmclass>ROX-Filer</wmclass>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="PCMan File Manager">
+ <action name="Execute">
+ <command>pcmanfm</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+</menu>
+
+<menu id="apps-graphics-menu" label="Graphics">
+ <item label="Gimp">
+ <action name="Execute">
+ <command>gimp</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Gwenview">
+ <action name="Execute">
+ <command>gwenview</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Dia Diagram Editor">
+ <action name="Execute">
+ <command>dia</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+ <item label="Inkscape">
+ <action name="Execute">
+ <command>inkscape</command>
+ <startupnotify>
+ <enabled>yes</enabled>
+ </startupnotify>
+ </action>
+ </item>
+</menu>
+
+<menu id="system-menu" label="System">
+ <item label="Openbox Configuration Manager">
+ <action name="Execute">
+ <command>obconf</command>
+ <startupnotify><enabled>yes</enabled></startupnotify>
+ </action>
+ </item>
+ <item label="Gnome Control Center">
+ <action name="Execute">
+ <command>gnome-control-center</command>
+ <startupnotify><enabled>yes</enabled></startupnotify>
+ </action>
+ </item>
+ <item label="KDE Control Center">
+ <action name="Execute">
+ <command>kcontrol</command>
+ <startupnotify><enabled>yes</enabled></startupnotify>
+ </action>
+ </item>
+ <item label="Xfce Settings">
+ <action name="Execute">
+ <command>xfce-setting-show</command>
+ <startupnotify><enabled>yes</enabled></startupnotify>
+ </action>
+ </item>
+ <item label="Manage Cups Printers">
+ <action name="Execute">
+ <command>xdg-open http://localhost:631/</command>
+ <startupnotify>
+ <enabled>no</enabled>
+ <icon>cups</icon>
+ </startupnotify>
+ </action>
+ </item>
+ <separator />
+ <item label="Reconfigure Openbox">
+ <action name="Reconfigure" />
+ </item>
+</menu>
+
+<menu id="root-menu" label="Openbox 3">
+ <separator label="Applications" />
+ <menu id="apps-accessories-menu"/>
+ <menu id="apps-editors-menu"/>
+ <menu id="apps-graphics-menu"/>
+ <menu id="apps-net-menu"/>
+ <menu id="apps-office-menu"/>
+ <menu id="apps-multimedia-menu"/>
+ <menu id="apps-term-menu"/>
+ <menu id="apps-fileman-menu"/>
+ <separator label="System" />
+ <menu id="system-menu"/>
+ <separator />
+ <item label="Log Out">
+ <action name="Exit">
+ <prompt>yes</prompt>
+ </action>
+ </item>
+</menu>
+
+</openbox_menu>
diff --git a/data/menu.xsd b/data/menu.xsd
new file mode 100644
index 0000000..2ff76b7
--- /dev/null
+++ b/data/menu.xsd
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- XML Schema for the Openbox window manager menu file -->
+
+<!DOCTYPE schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN"
+ "http://www.w3.org/2001/XMLSchema.dtd" [
+<!ATTLIST schema xmlns:ob CDATA #IMPLIED>
+<!ENTITY % p "xsd:">
+<!ENTITY % s ":xsd">
+]>
+
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://openbox.org/3.4/menu"
+ xmlns:ob="http://openbox.org/3.4/menu"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified">
+ <!--
+ root node
+ -->
+ <xsd:element name="openbox_menu">
+ <xsd:complexType mixed="false">
+ <xsd:sequence maxOccurs="unbounded" minOccurs="1">
+ <xsd:element name="menu" type="ob:menu"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <!--
+ complex types
+ -->
+ <!-- menu -->
+ <xsd:complexType name="menu">
+ <xsd:choice maxOccurs="unbounded" minOccurs="0">
+ <xsd:element name="menu" type="ob:menu"/>
+ <xsd:element name="item" type="ob:item"/>
+ <xsd:element name="separator" type="ob:separator"/>
+ </xsd:choice>
+ <xsd:attribute name="label" type="xsd:string" use="optional"/>
+ <xsd:attribute name="execute" type="xsd:string" use="optional"/>
+ <xsd:attribute name="id" type="xsd:string" use="required"/>
+ </xsd:complexType>
+
+ <!-- separator -->
+ <xsd:complexType name="separator">
+ <xsd:attribute name="label" type="xsd:string" use="optional"/>
+ </xsd:complexType>
+
+ <!-- empty -->
+ <xsd:complexType name="empty">
+ <xsd:complexContent>
+ <xsd:restriction base="xsd:anyType"/>
+ </xsd:complexContent>
+ </xsd:complexType>
+
+ <!-- item -->
+ <xsd:complexType name="item">
+ <xsd:sequence>
+ <xsd:element name="action">
+ <xsd:complexType>
+ <xsd:all>
+ <xsd:element minOccurs="0" name="execute" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="startupnotify" type="ob:notify"/>
+ <xsd:element minOccurs="0" name="command" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="allDesktops" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="menu" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="delta" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="x" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="y" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="left" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="right" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="up" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="down" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="desktop">
+ <xsd:simpleType>
+ <xsd:union memberTypes="xsd:integer ob:bool"/>
+ </xsd:simpleType>
+ </xsd:element>
+ <xsd:element minOccurs="0" name="edge" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="wrap" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="follow" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="dialog" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="panels" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="here" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="linear" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="group" type="ob:bool"/>
+ </xsd:all>
+ <xsd:attribute name="name" type="ob:actionname" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:sequence>
+ <xsd:attribute name="label" type="xsd:string" use="required"/>
+ </xsd:complexType>
+
+ <!-- startupnotify -->
+ <xsd:complexType name="startupnotify">
+ <xsd:all>
+ <xsd:element minOccurs="1" name="enabled" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="icon" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="wmclass" type="xsd:string"/>
+ </xsd:all>
+ </xsd:complexType>
+ <xsd:simpleType name="bool">
+ <!-- this is copied to maximization. Keep that in sync. -->
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="yes"/>
+ <xsd:enumeration value="no"/>
+ <xsd:enumeration value="true"/>
+ <xsd:enumeration value="false"/>
+ <xsd:enumeration value="on"/>
+ <xsd:enumeration value="off"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:complexType name="notify">
+ <xsd:all>
+ <xsd:element minOccurs="0" name="enabled" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="name" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="icon" type="xsd:string"/>
+ </xsd:all>
+ </xsd:complexType>
+ <!--
+ simple types / restrictions
+ -->
+ <xsd:simpleType name="actionname">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="[Aa][Cc][Tt][Ii][Vv][Aa][Tt][Ee]"/>
+ <xsd:pattern value="[Bb][Rr][Ee][Aa][Kk][Cc][Hh][Rr][Oo][Oo][Tt]"/>
+ <xsd:pattern value="[Cc][Ll][Oo][Ss][Ee]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Dd][Oo][Ww][Nn]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Ll][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Ll][Ee][Ff][Tt]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Nn][Ee][Xx][Tt]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Pp][Rr][Ee][Vv][Ii][Oo][Uu][Ss]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Rr][Ii][Gg][Hh][Tt]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Uu][Pp]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ee][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Nn][Oo][Rr][Tt][Hh]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Nn][Oo][Rr][Tt][Hh][Ee][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Nn][Oo][Rr][Tt][Hh][Ww][Ee][Ss][Tt]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ss][Oo][Uu][Tt][Hh]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ss][Oo][Uu][Tt][Hh][Ee][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ss][Oo][Uu][Tt][Hh][Ww][Ee][Ss][Tt]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ww][Ee][Ss][Tt]"/>
+ <xsd:pattern value="[Ee][Xx][Ee][Cc][Uu][Tt][Ee]"/>
+ <xsd:pattern value="[Ee][Xx][Ii][Tt]"/>
+ <xsd:pattern value="[Ss][Ee][Ss][Ss][Ii][Oo][Nn][Ll][Oo][Gg][Oo][Uu][Tt]"/>
+ <xsd:pattern value="[Ff][Oo][Cc][Uu][Ss]"/>
+ <xsd:pattern value="[Ff][Oo][Cc][Uu][Ss][Tt][Oo][Bb][Oo][Tt][Tt][Oo][Mm]"/>
+ <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Ee][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Nn][Oo][Rr][Tt][Hh]"/>
+ <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Ss][Oo][Uu][Tt][Hh]"/>
+ <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Ww][Ee][Ss][Tt]"/>
+ <xsd:pattern value="[Ii][Cc][Oo][Nn][Ii][Ff][Yy]"/>
+ <xsd:pattern value="[Kk][Ii][Ll][Ll]"/>
+ <xsd:pattern value="[Ll][Oo][Ww][Ee][Rr]"/>
+ <xsd:pattern value="[Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Ff][Uu][Ll][Ll]"/>
+ <xsd:pattern value="[Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Hh][Oo][Rr][Zz]"/>
+ <xsd:pattern value="[Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Vv][Ee][Rr][Tt]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Hh][Oo][Rr][Zz]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Vv][Ee][Rr][Tt]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Cc][Ee][Nn][Tt][Ee][Rr]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Ee][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Nn][Oo][Rr][Tt][Hh]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Ss][Oo][Uu][Tt][Hh]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Ww][Ee][Ss][Tt]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Ee][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Nn][Oo][Rr][Tt][Hh]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Ss][Oo][Uu][Tt][Hh]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Ww][Ee][Ss][Tt]"/>
+ <xsd:pattern value="[Nn][Ee][Xx][Tt][Ww][Ii][Nn][Dd][Oo][Ww]"/>
+ <xsd:pattern value="[Pp][Rr][Ee][Vv][Ii][Oo][Uu][Ss][Ww][Ii][Nn][Dd][Oo][Ww]"/>
+ <xsd:pattern value="[Rr][Aa][Ii][Ss][Ee]"/>
+ <xsd:pattern value="[Rr][Aa][Ii][Ss][Ee][Ll][Oo][Ww][Ee][Rr]"/>
+ <xsd:pattern value="[Rr][Ee][Cc][Oo][Nn][Ff][Ii][Gg][Uu][Rr][Ee]"/>
+ <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee]"/>
+ <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee]"/>
+ <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Hh][Oo][Rr][Zz]"/>
+ <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Vv][Ee][Rr][Tt]"/>
+ <xsd:pattern value="[Rr][Ee][Ss][Tt][Aa][Rr][Tt]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Bb][Oo][Tt][Tt][Oo][Mm][Ll][Aa][Yy][Ee][Rr]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Dd][Oo][Ww][Nn]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Ll][Ee][Ff][Tt]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Nn][Ee][Xx][Tt]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Pp][Rr][Ee][Vv][Ii][Oo][Uu][Ss]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Rr][Ii][Gg][Hh][Tt]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Uu][Pp]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Nn][Oo][Rr][Mm][Aa][Ll][Ll][Aa][Yy][Ee][Rr]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Tt][Oo][Pp][Ll][Aa][Yy][Ee][Rr]"/>
+ <xsd:pattern value="[Ss][Hh][Aa][Dd][Ee]"/>
+ <xsd:pattern value="[Ss][Hh][Aa][Dd][Ee][Ll][Oo][Ww][Ee][Rr]"/>
+ <xsd:pattern value="[Ss][Hh][Oo][Ww][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/>
+ <xsd:pattern value="[Ss][Hh][Oo][Ww][Mm][Ee][Nn][Uu]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Aa][Ll][Ww][Aa][Yy][Ss][Oo][Nn][Bb][Oo][Tt][Tt][Oo][Mm]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Aa][Ll][Ww][Aa][Yy][Ss][Oo][Nn][Tt][Oo][Pp]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Dd][Ee][Cc][Oo][Rr][Aa][Tt][Ii][Oo][Nn][Ss]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Dd][Oo][Cc][Kk][Aa][Uu][Tt][Oo][Hh][Ii][Dd][Ee]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Ff][Uu][Ll][Ll][Ss][Cc][Rr][Ee][Ee][Nn]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Ff][Uu][Ll][Ll]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Hh][Oo][Rr][Zz]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Vv][Ee][Rr][Tt]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Oo][Mm][Nn][Ii][Pp][Rr][Ee][Ss][Ee][Nn][Tt]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Ss][Hh][Aa][Dd][Ee]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Ss][Hh][Oo][Ww][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/>
+ <xsd:pattern value="[Uu][Nn][Ff][Oo][Cc][Uu][Ss]"/>
+ <xsd:pattern value="[Uu][Nn][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Ff][Uu][Ll][Ll]"/>
+ <xsd:pattern value="[Uu][Nn][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Hh][Oo][Rr][Zz]"/>
+ <xsd:pattern value="[Uu][Nn][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Vv][Ee][Rr][Tt]"/>
+ <xsd:pattern value="[Uu][Nn][Ss][Hh][Aa][Dd][Ee]"/>
+ <xsd:pattern value="[Uu][Nn][Ss][Hh][Aa][Dd][Ee][Rr][Aa][Ii][Ss][Ee]"/>
+ <xsd:pattern value="[Uu][Nn][Ss][Hh][Oo][Ww][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+</xsd:schema>
diff --git a/data/openbox.desktop b/data/openbox.desktop
new file mode 100644
index 0000000..d49ae22
--- /dev/null
+++ b/data/openbox.desktop
@@ -0,0 +1,16 @@
+[Desktop Entry]
+Type=Application
+Encoding=UTF-8
+Name=Openbox
+Exec=openbox
+Icon=openbox
+NoDisplay=true
+# name we put on the WM spec check window
+X-GNOME-WMName=Openbox
+# gnome-session autostart
+X-GNOME-Autostart-Phase=WindowManager
+X-GNOME-Provides=windowmanager
+# Ubuntu stuff
+X-Ubuntu-Gettext-Domain=openbox
+# back compat
+X-GNOME-Autostart-Notify=true
diff --git a/data/openbox.png b/data/openbox.png
new file mode 100644
index 0000000..70d1f07
--- /dev/null
+++ b/data/openbox.png
Binary files differ
diff --git a/data/rc.xml b/data/rc.xml
new file mode 100644
index 0000000..209cc2d
--- /dev/null
+++ b/data/rc.xml
@@ -0,0 +1,730 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Do not edit this file, it will be overwritten on install.
+ Copy the file to $HOME/.config/openbox/ instead. -->
+
+<openbox_config xmlns="http://openbox.org/3.4/rc"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<resistance>
+ <strength>10</strength>
+ <screen_edge_strength>20</screen_edge_strength>
+</resistance>
+
+<focus>
+ <focusNew>yes</focusNew>
+ <!-- always try to focus new windows when they appear. other rules do
+ apply -->
+ <followMouse>no</followMouse>
+ <!-- move focus to a window when you move the mouse into it -->
+ <focusLast>yes</focusLast>
+ <!-- focus the last used window when changing desktops, instead of the one
+ under the mouse pointer. when followMouse is enabled -->
+ <underMouse>no</underMouse>
+ <!-- move focus under the mouse, even when the mouse is not moving -->
+ <focusDelay>200</focusDelay>
+ <!-- when followMouse is enabled, the mouse must be inside the window for
+ this many milliseconds (1000 = 1 sec) before moving focus to it -->
+ <raiseOnFocus>no</raiseOnFocus>
+ <!-- when followMouse is enabled, and a window is given focus by moving the
+ mouse into it, also raise the window -->
+</focus>
+
+<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
+ the active window is, 'Primary' - only on the primary monitor -->
+ <primaryMonitor>1</primaryMonitor>
+ <!-- The monitor where Openbox should place popup dialogs such as the
+ focus cycling popup, or the desktop switch popup. It can be an index
+ from 1, specifying a particular monitor. Or it can be one of the
+ following: 'Mouse' - where the mouse is, or
+ 'Active' - where the active window is -->
+</placement>
+
+<theme>
+ <name>Clearlooks</name>
+ <titleLayout>NLIMC</titleLayout>
+ <!--
+ available characters are NDSLIMC, each can occur at most once.
+ N: window icon
+ L: window label (AKA title).
+ I: iconify
+ M: maximize
+ C: close
+ S: shade (roll up/down)
+ D: omnipresent (on all desktops).
+ -->
+ <keepBorder>yes</keepBorder>
+ <animateIconify>yes</animateIconify>
+ <font place="ActiveWindow">
+ <name>sans</name>
+ <size>8</size>
+ <!-- font size in points -->
+ <weight>bold</weight>
+ <!-- 'bold' or 'normal' -->
+ <slant>normal</slant>
+ <!-- 'italic' or 'normal' -->
+ </font>
+ <font place="InactiveWindow">
+ <name>sans</name>
+ <size>8</size>
+ <!-- font size in points -->
+ <weight>bold</weight>
+ <!-- 'bold' or 'normal' -->
+ <slant>normal</slant>
+ <!-- 'italic' or 'normal' -->
+ </font>
+ <font place="MenuHeader">
+ <name>sans</name>
+ <size>9</size>
+ <!-- font size in points -->
+ <weight>normal</weight>
+ <!-- 'bold' or 'normal' -->
+ <slant>normal</slant>
+ <!-- 'italic' or 'normal' -->
+ </font>
+ <font place="MenuItem">
+ <name>sans</name>
+ <size>9</size>
+ <!-- font size in points -->
+ <weight>normal</weight>
+ <!-- 'bold' or 'normal' -->
+ <slant>normal</slant>
+ <!-- 'italic' or 'normal' -->
+ </font>
+ <font place="ActiveOnScreenDisplay">
+ <name>sans</name>
+ <size>9</size>
+ <!-- font size in points -->
+ <weight>bold</weight>
+ <!-- 'bold' or 'normal' -->
+ <slant>normal</slant>
+ <!-- 'italic' or 'normal' -->
+ </font>
+ <font place="InactiveOnScreenDisplay">
+ <name>sans</name>
+ <size>9</size>
+ <!-- font size in points -->
+ <weight>bold</weight>
+ <!-- 'bold' or 'normal' -->
+ <slant>normal</slant>
+ <!-- 'italic' or 'normal' -->
+ </font>
+</theme>
+
+<desktops>
+ <!-- this stuff is only used at startup, pagers allow you to change them
+ during a session
+
+ these are default values to use when other ones are not already set
+ by other applications, or saved in your session
+
+ use obconf if you want to change these without having to log out
+ and back in -->
+ <number>4</number>
+ <firstdesk>1</firstdesk>
+ <names>
+ <!-- set names up here if you want to, like this:
+ <name>desktop 1</name>
+ <name>desktop 2</name>
+ -->
+ </names>
+ <popupTime>875</popupTime>
+ <!-- The number of milliseconds to show the popup for when switching
+ desktops. Set this to 0 to disable the popup. -->
+</desktops>
+
+<resize>
+ <drawContents>yes</drawContents>
+ <popupShow>Nonpixel</popupShow>
+ <!-- 'Always', 'Never', or 'Nonpixel' (xterms and such) -->
+ <popupPosition>Center</popupPosition>
+ <!-- 'Center', 'Top', or 'Fixed' -->
+ <popupFixedPosition>
+ <!-- these are used if popupPosition is set to 'Fixed' -->
+
+ <x>10</x>
+ <!-- positive number for distance from left edge, negative number for
+ distance from right edge, or 'Center' -->
+ <y>10</y>
+ <!-- positive number for distance from top edge, negative number for
+ distance from bottom edge, or 'Center' -->
+ </popupFixedPosition>
+</resize>
+
+<!-- You can reserve a portion of your screen where windows will not cover when
+ they are maximized, or when they are initially placed.
+ Many programs reserve space automatically, but you can use this in other
+ cases. -->
+<margins>
+ <top>0</top>
+ <bottom>0</bottom>
+ <left>0</left>
+ <right>0</right>
+</margins>
+
+<dock>
+ <position>TopLeft</position>
+ <!-- (Top|Bottom)(Left|Right|)|Top|Bottom|Left|Right|Floating -->
+ <floatingX>0</floatingX>
+ <floatingY>0</floatingY>
+ <noStrut>no</noStrut>
+ <stacking>Above</stacking>
+ <!-- 'Above', 'Normal', or 'Below' -->
+ <direction>Vertical</direction>
+ <!-- 'Vertical' or 'Horizontal' -->
+ <autoHide>no</autoHide>
+ <hideDelay>300</hideDelay>
+ <!-- in milliseconds (1000 = 1 second) -->
+ <showDelay>300</showDelay>
+ <!-- in milliseconds (1000 = 1 second) -->
+ <moveButton>Middle</moveButton>
+ <!-- 'Left', 'Middle', 'Right' -->
+</dock>
+
+<keyboard>
+ <chainQuitKey>C-g</chainQuitKey>
+
+ <!-- Keybindings for desktop switching -->
+ <keybind key="C-A-Left">
+ <action name="GoToDesktop"><to>left</to><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="C-A-Right">
+ <action name="GoToDesktop"><to>right</to><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="C-A-Up">
+ <action name="GoToDesktop"><to>up</to><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="C-A-Down">
+ <action name="GoToDesktop"><to>down</to><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="S-A-Left">
+ <action name="SendToDesktop"><to>left</to><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="S-A-Right">
+ <action name="SendToDesktop"><to>right</to><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="S-A-Up">
+ <action name="SendToDesktop"><to>up</to><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="S-A-Down">
+ <action name="SendToDesktop"><to>down</to><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="W-F1">
+ <action name="GoToDesktop"><to>1</to></action>
+ </keybind>
+ <keybind key="W-F2">
+ <action name="GoToDesktop"><to>2</to></action>
+ </keybind>
+ <keybind key="W-F3">
+ <action name="GoToDesktop"><to>3</to></action>
+ </keybind>
+ <keybind key="W-F4">
+ <action name="GoToDesktop"><to>4</to></action>
+ </keybind>
+ <keybind key="W-d">
+ <action name="ToggleShowDesktop"/>
+ </keybind>
+
+ <!-- Keybindings for windows -->
+ <keybind key="A-F4">
+ <action name="Close"/>
+ </keybind>
+ <keybind key="A-Escape">
+ <action name="Lower"/>
+ <action name="FocusToBottom"/>
+ <action name="Unfocus"/>
+ </keybind>
+ <keybind key="A-space">
+ <action name="ShowMenu"><menu>client-menu</menu></action>
+ </keybind>
+
+ <!-- Keybindings for window switching -->
+ <keybind key="A-Tab">
+ <action name="NextWindow">
+ <finalactions>
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="Unshade"/>
+ </finalactions>
+ </action>
+ </keybind>
+ <keybind key="A-S-Tab">
+ <action name="PreviousWindow">
+ <finalactions>
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="Unshade"/>
+ </finalactions>
+ </action>
+ </keybind>
+ <keybind key="C-A-Tab">
+ <action name="NextWindow">
+ <panels>yes</panels><desktop>yes</desktop>
+ <finalactions>
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="Unshade"/>
+ </finalactions>
+ </action>
+ </keybind>
+
+ <!-- Keybindings for window switching with the arrow keys -->
+ <keybind key="W-S-Right">
+ <action name="DirectionalCycleWindows">
+ <direction>right</direction>
+ </action>
+ </keybind>
+ <keybind key="W-S-Left">
+ <action name="DirectionalCycleWindows">
+ <direction>left</direction>
+ </action>
+ </keybind>
+ <keybind key="W-S-Up">
+ <action name="DirectionalCycleWindows">
+ <direction>up</direction>
+ </action>
+ </keybind>
+ <keybind key="W-S-Down">
+ <action name="DirectionalCycleWindows">
+ <direction>down</direction>
+ </action>
+ </keybind>
+
+ <!-- Keybindings for running applications -->
+ <keybind key="W-e">
+ <action name="Execute">
+ <startupnotify>
+ <enabled>true</enabled>
+ <name>Konqueror</name>
+ </startupnotify>
+ <command>kfmclient openProfile filemanagement</command>
+ </action>
+ </keybind>
+</keyboard>
+
+<mouse>
+ <dragThreshold>1</dragThreshold>
+ <!-- number of pixels the mouse must move before a drag begins -->
+ <doubleClickTime>500</doubleClickTime>
+ <!-- in milliseconds (1000 = 1 second) -->
+ <screenEdgeWarpTime>400</screenEdgeWarpTime>
+ <!-- Time before changing desktops when the pointer touches the edge of the
+ screen while moving a window, in milliseconds (1000 = 1 second).
+ Set this to 0 to disable warping -->
+ <screenEdgeWarpMouse>false</screenEdgeWarpMouse>
+ <!-- Set this to TRUE to move the mouse pointer across the desktop when
+ switching due to hitting the edge of the screen -->
+
+ <context name="Frame">
+ <mousebind button="A-Left" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="A-Left" action="Click">
+ <action name="Unshade"/>
+ </mousebind>
+ <mousebind button="A-Left" action="Drag">
+ <action name="Move"/>
+ </mousebind>
+
+ <mousebind button="A-Right" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="Unshade"/>
+ </mousebind>
+ <mousebind button="A-Right" action="Drag">
+ <action name="Resize"/>
+ </mousebind>
+
+ <mousebind button="A-Middle" action="Press">
+ <action name="Lower"/>
+ <action name="FocusToBottom"/>
+ <action name="Unfocus"/>
+ </mousebind>
+
+ <mousebind button="A-Up" action="Click">
+ <action name="GoToDesktop"><to>previous</to></action>
+ </mousebind>
+ <mousebind button="A-Down" action="Click">
+ <action name="GoToDesktop"><to>next</to></action>
+ </mousebind>
+ <mousebind button="C-A-Up" action="Click">
+ <action name="GoToDesktop"><to>previous</to></action>
+ </mousebind>
+ <mousebind button="C-A-Down" action="Click">
+ <action name="GoToDesktop"><to>next</to></action>
+ </mousebind>
+ <mousebind button="A-S-Up" action="Click">
+ <action name="SendToDesktop"><to>previous</to></action>
+ </mousebind>
+ <mousebind button="A-S-Down" action="Click">
+ <action name="SendToDesktop"><to>next</to></action>
+ </mousebind>
+ </context>
+
+ <context name="Titlebar">
+ <mousebind button="Left" action="Drag">
+ <action name="Move"/>
+ </mousebind>
+ <mousebind button="Left" action="DoubleClick">
+ <action name="ToggleMaximize"/>
+ </mousebind>
+
+ <mousebind button="Up" action="Click">
+ <action name="if">
+ <shaded>no</shaded>
+ <then>
+ <action name="Shade"/>
+ <action name="FocusToBottom"/>
+ <action name="Unfocus"/>
+ <action name="Lower"/>
+ </then>
+ </action>
+ </mousebind>
+ <mousebind button="Down" action="Click">
+ <action name="if">
+ <shaded>yes</shaded>
+ <then>
+ <action name="Unshade"/>
+ <action name="Raise"/>
+ </then>
+ </action>
+ </mousebind>
+ </context>
+
+ <context name="Titlebar Top Right Bottom Left TLCorner TRCorner BRCorner BLCorner">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="Unshade"/>
+ </mousebind>
+
+ <mousebind button="Middle" action="Press">
+ <action name="Lower"/>
+ <action name="FocusToBottom"/>
+ <action name="Unfocus"/>
+ </mousebind>
+
+ <mousebind button="Right" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="ShowMenu"><menu>client-menu</menu></action>
+ </mousebind>
+ </context>
+
+ <context name="Top">
+ <mousebind button="Left" action="Drag">
+ <action name="Resize"><edge>top</edge></action>
+ </mousebind>
+ </context>
+
+ <context name="Left">
+ <mousebind button="Left" action="Drag">
+ <action name="Resize"><edge>left</edge></action>
+ </mousebind>
+ </context>
+
+ <context name="Right">
+ <mousebind button="Left" action="Drag">
+ <action name="Resize"><edge>right</edge></action>
+ </mousebind>
+ </context>
+
+ <context name="Bottom">
+ <mousebind button="Left" action="Drag">
+ <action name="Resize"><edge>bottom</edge></action>
+ </mousebind>
+
+ <mousebind button="Right" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="ShowMenu"><menu>client-menu</menu></action>
+ </mousebind>
+ </context>
+
+ <context name="TRCorner BRCorner TLCorner BLCorner">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="Unshade"/>
+ </mousebind>
+ <mousebind button="Left" action="Drag">
+ <action name="Resize"/>
+ </mousebind>
+ </context>
+
+ <context name="Client">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Middle" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Right" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ </mousebind>
+ </context>
+
+ <context name="Icon">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="Unshade"/>
+ <action name="ShowMenu"><menu>client-menu</menu></action>
+ </mousebind>
+ <mousebind button="Right" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="ShowMenu"><menu>client-menu</menu></action>
+ </mousebind>
+ </context>
+
+ <context name="AllDesktops">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="Unshade"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="ToggleOmnipresent"/>
+ </mousebind>
+ </context>
+
+ <context name="Shade">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="ToggleShade"/>
+ </mousebind>
+ </context>
+
+ <context name="Iconify">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="Iconify"/>
+ </mousebind>
+ </context>
+
+ <context name="Maximize">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="Unshade"/>
+ </mousebind>
+ <mousebind button="Middle" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="Unshade"/>
+ </mousebind>
+ <mousebind button="Right" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="Unshade"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="ToggleMaximize"/>
+ </mousebind>
+ <mousebind button="Middle" action="Click">
+ <action name="ToggleMaximize"><direction>vertical</direction></action>
+ </mousebind>
+ <mousebind button="Right" action="Click">
+ <action name="ToggleMaximize"><direction>horizontal</direction></action>
+ </mousebind>
+ </context>
+
+ <context name="Close">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ <action name="Unshade"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="Close"/>
+ </mousebind>
+ </context>
+
+ <context name="Desktop">
+ <mousebind button="Up" action="Click">
+ <action name="GoToDesktop"><to>previous</to></action>
+ </mousebind>
+ <mousebind button="Down" action="Click">
+ <action name="GoToDesktop"><to>next</to></action>
+ </mousebind>
+
+ <mousebind button="A-Up" action="Click">
+ <action name="GoToDesktop"><to>previous</to></action>
+ </mousebind>
+ <mousebind button="A-Down" action="Click">
+ <action name="GoToDesktop"><to>next</to></action>
+ </mousebind>
+ <mousebind button="C-A-Up" action="Click">
+ <action name="GoToDesktop"><to>previous</to></action>
+ </mousebind>
+ <mousebind button="C-A-Down" action="Click">
+ <action name="GoToDesktop"><to>next</to></action>
+ </mousebind>
+
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Right" action="Press">
+ <action name="Focus"/>
+ <action name="Raise"/>
+ </mousebind>
+ </context>
+
+ <context name="Root">
+ <!-- Menus -->
+ <mousebind button="Middle" action="Press">
+ <action name="ShowMenu"><menu>client-list-combined-menu</menu></action>
+ </mousebind>
+ <mousebind button="Right" action="Press">
+ <action name="ShowMenu"><menu>root-menu</menu></action>
+ </mousebind>
+ </context>
+
+ <context name="MoveResize">
+ <mousebind button="Up" action="Click">
+ <action name="GoToDesktop"><to>previous</to></action>
+ </mousebind>
+ <mousebind button="Down" action="Click">
+ <action name="GoToDesktop"><to>next</to></action>
+ </mousebind>
+ <mousebind button="A-Up" action="Click">
+ <action name="GoToDesktop"><to>previous</to></action>
+ </mousebind>
+ <mousebind button="A-Down" action="Click">
+ <action name="GoToDesktop"><to>next</to></action>
+ </mousebind>
+ </context>
+</mouse>
+
+<menu>
+ <!-- You can specify more than one menu file in here and they are all loaded,
+ just don't make menu ids clash or, well, it'll be kind of pointless -->
+
+ <!-- default menu file (or custom one in $HOME/.config/openbox/) -->
+ <file>menu.xml</file>
+ <hideDelay>200</hideDelay>
+ <!-- if a press-release lasts longer than this setting (in milliseconds), the
+ menu is hidden again -->
+ <middle>no</middle>
+ <!-- center submenus vertically about the parent entry -->
+ <submenuShowDelay>100</submenuShowDelay>
+ <!-- time to delay before showing a submenu after hovering over the parent
+ entry.
+ if this is a negative value, then the delay is infinite and the
+ 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 -->
+ 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>
+ <!-- controls if icons appear in the client-list-(combined-)menu -->
+ <manageDesktops>yes</manageDesktops>
+ <!-- show the manage desktops section in the client-list-(combined-)menu -->
+</menu>
+
+<applications>
+<!--
+ # this is an example with comments through out. use these to make your
+ # own rules, but without the comments of course.
+ # you may use one or more of the name/class/role/title/type rules to specify
+ # windows to match
+
+ <application name="the window's _OB_APP_NAME property (see obxprop)"
+ class="the window's _OB_APP_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)..
+ (if unspecified, then it is 'dialog' for child windows)">
+ # you may set only one of name/class/role/title/type, or you may use more
+ # than one together to restrict your matches.
+
+ # the name, class, role, and title use simple wildcard matching such as those
+ # used by a shell. you can use * to match any characters and ? to match
+ # any single character.
+
+ # the type is one of: normal, dialog, splash, utility, menu, toolbar, dock,
+ # or desktop
+
+ # when multiple rules match a window, they will all be applied, in the
+ # order that they appear in this list
+
+
+ # each rule element can be left out or set to 'default' to specify to not
+ # change that attribute of the window
+
+ <decor>yes</decor>
+ # enable or disable window decorations
+
+ <shade>no</shade>
+ # make the window shaded when it appears, or not
+
+ <position force="no">
+ # the position is only used if both an x and y coordinate are provided
+ # (and not set to 'default')
+ # when force is "yes", then the window will be placed here even if it
+ # says you want it placed elsewhere. this is to override buggy
+ # 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).
+ <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>
+
+ <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
+ # restrictions may apply, but Openbox will try to
+
+ <desktop>1</desktop>
+ # 1 is the first desktop, 'all' for all desktops
+
+ <layer>normal</layer>
+ # 'above', 'normal', or 'below'
+
+ <iconic>no</iconic>
+ # make the window iconified when it appears, or not
+
+ <skip_pager>no</skip_pager>
+ # asks to not be shown in pagers
+
+ <skip_taskbar>no</skip_taskbar>
+ # asks to not be shown in taskbars. window cycling actions will also
+ # skip past such windows
+
+ <fullscreen>yes</fullscreen>
+ # make the window in fullscreen mode when it appears
+
+ <maximized>true</maximized>
+ # 'Horizontal', 'Vertical' or boolean (yes/no)
+ </application>
+
+ # end of the example
+-->
+</applications>
+
+</openbox_config>
diff --git a/data/rc.xsd b/data/rc.xsd
new file mode 100644
index 0000000..ad96994
--- /dev/null
+++ b/data/rc.xsd
@@ -0,0 +1,551 @@
+<?xml version="1.0" encoding="UTF-8"?> <!-- -*- nxml -*- -->
+
+<!-- XML Schema for the Openbox window manager configuration file -->
+
+<!DOCTYPE schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN"
+ "http://www.w3.org/2001/XMLSchema.dtd" [
+<!ATTLIST schema xmlns:ob CDATA #IMPLIED>
+<!ENTITY % p "xsd:">
+<!ENTITY % s ":xsd">
+]>
+
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://openbox.org/4.0/rc"
+ xmlns:ob="http://openbox.org/4.0/rc"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified">
+ <!--
+ root node
+ -->
+ <xsd:element name="openbox_config">
+ <xsd:annotation>
+ <xsd:documentation>all these elements are expected in a openbox config file</xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:all>
+ <xsd:element name="resistance" type="ob:resistance"/>
+ <xsd:element name="focus" type="ob:focus"/>
+ <xsd:element name="placement" type="ob:placement"/>
+ <xsd:element name="theme" type="ob:theme"/>
+ <xsd:element name="desktops" type="ob:desktops"/>
+ <xsd:element name="resize" type="ob:resize"/>
+ <xsd:element minOccurs="0" name="margins" type="ob:margins"/>
+ <xsd:element name="dock" type="ob:dock"/>
+ <xsd:element name="keyboard" type="ob:keyboard"/>
+ <xsd:element name="mouse" type="ob:mouse"/>
+ <xsd:element name="menu" type="ob:menu"/>
+ <xsd:element name="applications" type="ob:applications"/>
+ </xsd:all>
+ </xsd:complexType>
+ </xsd:element>
+ <!--
+ complex types
+ -->
+ <xsd:complexType name="resistance">
+ <xsd:annotation>
+ <xsd:documentation>defines behaviour of windows when close to each other or the screen edge</xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <xsd:element minOccurs="0" name="strength" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="screen_edge_strength" type="xsd:integer"/>
+ </xsd:all>
+ </xsd:complexType>
+ <xsd:complexType name="focus">
+ <xsd:annotation>
+ <xsd:documentation>defines aspects of window focus</xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <xsd:element minOccurs="0" name="focusNew" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="focusLast" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="followMouse" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="underMouse" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="focusDelay" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="raiseOnFocus" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="unfocusOnLeave" type="ob:bool"/>
+ </xsd:all>
+ </xsd:complexType>
+ <xsd:complexType name="placement">
+ <xsd:annotation>
+ <xsd:documentation>defines how new windows are placed</xsd:documentation>
+ </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:sequence>
+ </xsd:complexType>
+ <xsd:complexType name="margins">
+ <xsd:annotation>
+ <xsd:documentation>defines desktop margins</xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <xsd:element minOccurs="0" name="top" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="left" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="right" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="bottom" type="xsd:integer"/>
+ </xsd:all>
+ </xsd:complexType>
+ <xsd:complexType name="theme">
+ <xsd:sequence>
+ <xsd:element minOccurs="0" name="name" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="titleLayout" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="keepBorder" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="animateIconify" type="ob:bool"/>
+ <xsd:element minOccurs="0" maxOccurs="unbounded" name="font" type="ob:font"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ <xsd:complexType name="font">
+ <xsd:all>
+ <xsd:element minOccurs="0" name="name" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="size" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="weight" type="ob:fontweight"/>
+ <xsd:element minOccurs="0" name="slant" type="ob:fontslant"/>
+ </xsd:all>
+ <xsd:attribute name="place" type="ob:fontplace" use="required"/>
+ </xsd:complexType>
+ <xsd:complexType name="desktops">
+ <xsd:annotation>
+ <xsd:documentation>defines the number and names of desktops</xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <xsd:element minOccurs="0" name="number" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="firstdesk" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="names">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element minOccurs="0" maxOccurs="unbounded" name="name" type="xsd:string"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element minOccurs="0" name="popupTime" type="xsd:integer"/>
+ </xsd:all>
+ </xsd:complexType>
+ <xsd:complexType name="resize">
+ <xsd:all>
+ <xsd:element minOccurs="0" name="drawContents" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="popupShow" type="ob:popupshow"/>
+ <xsd:element minOccurs="0" name="popupPosition" type="ob:popupposition"/>
+ <xsd:element minOccurs="0" name="popupFixedPosition" type="ob:popupfixedposition"/>
+ </xsd:all>
+ </xsd:complexType>
+ <xsd:complexType name="popupfixedposition">
+ <xsd:all>
+ <xsd:element minOccurs="0" name="x" type="ob:center_or_int"/>
+ <xsd:element minOccurs="0" name="y" type="ob:center_or_int"/>
+ </xsd:all>
+ </xsd:complexType>
+ <xsd:complexType name="dock">
+ <xsd:all>
+ <xsd:element minOccurs="0" name="position" type="ob:dock_position"/>
+ <xsd:element minOccurs="0" name="floatingX" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="floatingY" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="noStrut" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="stacking" type="ob:layer"/>
+ <xsd:element minOccurs="0" name="direction" type="ob:direction"/>
+ <xsd:element minOccurs="0" name="autoHide" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="hideDelay" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="showDelay" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="moveButton" type="ob:button"/>
+ </xsd:all>
+ </xsd:complexType>
+ <xsd:complexType name="action">
+ <xsd:all>
+ <xsd:element minOccurs="0" name="execute" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="startupnotify" type="ob:notify"/>
+ <xsd:element minOccurs="0" name="command" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="allDesktops" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="menu" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="delta" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="x" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="y" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="left" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="right" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="up" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="down" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="desktop">
+ <xsd:simpleType>
+ <xsd:union memberTypes="xsd:integer ob:bool"/>
+ </xsd:simpleType>
+ </xsd:element>
+ <xsd:element minOccurs="0" name="edge" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="wrap" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="follow" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="dialog" type="ob:dialogtype"/>
+ <xsd:element minOccurs="0" name="panels" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="here" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="linear" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="group" type="ob:bool"/>
+ </xsd:all>
+ <xsd:attribute name="name" type="ob:actionname" use="required"/>
+ </xsd:complexType>
+ <xsd:complexType name="keybind">
+ <xsd:choice>
+ <xsd:element maxOccurs="unbounded" name="action" type="ob:action"/>
+ <xsd:element maxOccurs="unbounded" name="keybind" type="ob:keybind"/>
+ </xsd:choice>
+ <xsd:attribute name="chroot" type="ob:bool"/>
+ <xsd:attribute name="key" type="ob:keyname" use="required"/>
+ </xsd:complexType>
+ <xsd:complexType name="keyboard">
+ <xsd:sequence>
+ <xsd:element minOccurs="0" name="chainQuitKey" type="ob:keyname"/>
+ <xsd:element maxOccurs="unbounded" name="keybind" type="ob:keybind"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ <xsd:complexType name="mousebind">
+ <xsd:sequence>
+ <xsd:element maxOccurs="unbounded" name="action" type="ob:action"/>
+ </xsd:sequence>
+ <xsd:attribute name="action" type="ob:mouseaction" use="required"/>
+ <xsd:attribute name="button" type="ob:button" use="required"/>
+ </xsd:complexType>
+ <xsd:complexType name="context">
+ <xsd:sequence>
+ <xsd:element maxOccurs="unbounded" name="mousebind" type="ob:mousebind"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="ob:contextname" use="required"/>
+ </xsd:complexType>
+ <xsd:complexType name="mouse">
+ <xsd:sequence>
+ <xsd:element minOccurs="0" name="dragThreshold" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="doubleClickTime" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="screenEdgeWarpTime" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="screenEdgeWarpMouse" type="ob:bool"/>
+ <xsd:element maxOccurs="unbounded" name="context" type="ob:context"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ <xsd:complexType name="menu">
+ <xsd:sequence>
+ <xsd:element maxOccurs="unbounded" name="file" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="hideDelay" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="middle" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="submenuShowDelay" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="showIcons" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="manageDesktops" type="ob:bool"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ <xsd:complexType name="window_position">
+ <xsd:all>
+ <xsd:element name="x" type="ob:center_or_int"/>
+ <xsd:element name="y" type="ob:center_or_int"/>
+ <xsd:element minOccurs="0" name="monitor" type="ob:mouse_or_int"/>
+ <xsd:element minOccurs="0" name="head" type="xsd:string"/>
+ </xsd:all>
+ <xsd:attribute name="force" type="ob:bool"/>
+ </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="focus" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="desktop" type="xsd:integer"/>
+ <xsd:element minOccurs="0" name="layer" type="ob:layer"/>
+ <xsd:element minOccurs="0" name="iconic" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="skip_pager" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="skip_taskbar" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="fullscreen" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="maximized" type="ob:maximization"/>
+ </xsd:all>
+ <!-- at least one of these must be present -->
+ <xsd:attribute name="role" type="xsd:string"/>
+ <xsd:attribute name="title" type="xsd:string"/>
+ <xsd:attribute name="type" type="ob:clienttype"/>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="class" type="xsd:string"/>
+ </xsd:complexType>
+ <xsd:complexType name="applications">
+ <xsd:sequence>
+ <xsd:element minOccurs="0" maxOccurs="unbounded" name="application" type="ob:application"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ <xsd:complexType name="notify">
+ <xsd:all>
+ <xsd:element minOccurs="0" name="enabled" type="ob:bool"/>
+ <xsd:element minOccurs="0" name="name" type="xsd:string"/>
+ <xsd:element minOccurs="0" name="icon" type="xsd:string"/>
+ </xsd:all>
+ </xsd:complexType>
+ <!--
+ simple types / restrictions
+ -->
+ <xsd:simpleType name="actionname">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="[Ii][Ff]"/>
+ <xsd:pattern value="[Aa][Cc][Tt][Ii][Vv][Aa][Tt][Ee]"/>
+ <xsd:pattern value="[Bb][Rr][Ee][Aa][Kk][Cc][Hh][Rr][Oo][Oo][Tt]"/>
+ <xsd:pattern value="[Cc][Ll][Oo][Ss][Ee]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Dd][Oo][Ww][Nn]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Ll][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Ll][Ee][Ff][Tt]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Nn][Ee][Xx][Tt]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Pp][Rr][Ee][Vv][Ii][Oo][Uu][Ss]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Rr][Ii][Gg][Hh][Tt]"/>
+ <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Uu][Pp]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ee][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Nn][Oo][Rr][Tt][Hh]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Nn][Oo][Rr][Tt][Hh][Ee][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Nn][Oo][Rr][Tt][Hh][Ww][Ee][Ss][Tt]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ss][Oo][Uu][Tt][Hh]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ss][Oo][Uu][Tt][Hh][Ee][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ss][Oo][Uu][Tt][Hh][Ww][Ee][Ss][Tt]"/>
+ <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ww][Ee][Ss][Tt]"/>
+ <xsd:pattern value="[Ee][Xx][Ee][Cc][Uu][Tt][Ee]"/>
+ <xsd:pattern value="[Ee][Xx][Ii][Tt]"/>
+ <xsd:pattern value="[Ss][Ee][Ss][Ss][Ii][Oo][Nn][Ll][Oo][Gg][Oo][Uu][Tt]"/>
+ <xsd:pattern value="[Ff][Oo][Cc][Uu][Ss]"/>
+ <xsd:pattern value="[Ff][Oo][Cc][Uu][Ss][Tt][Oo][Bb][Oo][Tt][Tt][Oo][Mm]"/>
+ <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Ee][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Nn][Oo][Rr][Tt][Hh]"/>
+ <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Ss][Oo][Uu][Tt][Hh]"/>
+ <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Ww][Ee][Ss][Tt]"/>
+ <xsd:pattern value="[Ii][Cc][Oo][Nn][Ii][Ff][Yy]"/>
+ <xsd:pattern value="[Kk][Ii][Ll][Ll]"/>
+ <xsd:pattern value="[Ll][Oo][Ww][Ee][Rr]"/>
+ <xsd:pattern value="[Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Ff][Uu][Ll][Ll]"/>
+ <xsd:pattern value="[Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Hh][Oo][Rr][Zz]"/>
+ <xsd:pattern value="[Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Vv][Ee][Rr][Tt]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Hh][Oo][Rr][Zz]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Vv][Ee][Rr][Tt]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Cc][Ee][Nn][Tt][Ee][Rr]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Ee][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Nn][Oo][Rr][Tt][Hh]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Ss][Oo][Uu][Tt][Hh]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Ww][Ee][Ss][Tt]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Ee][Aa][Ss][Tt]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Nn][Oo][Rr][Tt][Hh]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Ss][Oo][Uu][Tt][Hh]"/>
+ <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Ww][Ee][Ss][Tt]"/>
+ <xsd:pattern value="[Nn][Ee][Xx][Tt][Ww][Ii][Nn][Dd][Oo][Ww]"/>
+ <xsd:pattern value="[Pp][Rr][Ee][Vv][Ii][Oo][Uu][Ss][Ww][Ii][Nn][Dd][Oo][Ww]"/>
+ <xsd:pattern value="[Rr][Aa][Ii][Ss][Ee]"/>
+ <xsd:pattern value="[Rr][Aa][Ii][Ss][Ee][Ll][Oo][Ww][Ee][Rr]"/>
+ <xsd:pattern value="[Rr][Ee][Cc][Oo][Nn][Ff][Ii][Gg][Uu][Rr][Ee]"/>
+ <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee]"/>
+ <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee]"/>
+ <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Hh][Oo][Rr][Zz]"/>
+ <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Vv][Ee][Rr][Tt]"/>
+ <xsd:pattern value="[Rr][Ee][Ss][Tt][Aa][Rr][Tt]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Bb][Oo][Tt][Tt][Oo][Mm][Ll][Aa][Yy][Ee][Rr]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Dd][Oo][Ww][Nn]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Ll][Ee][Ff][Tt]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Nn][Ee][Xx][Tt]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Pp][Rr][Ee][Vv][Ii][Oo][Uu][Ss]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Rr][Ii][Gg][Hh][Tt]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Uu][Pp]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Nn][Oo][Rr][Mm][Aa][Ll][Ll][Aa][Yy][Ee][Rr]"/>
+ <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Tt][Oo][Pp][Ll][Aa][Yy][Ee][Rr]"/>
+ <xsd:pattern value="[Ss][Hh][Aa][Dd][Ee]"/>
+ <xsd:pattern value="[Ss][Hh][Aa][Dd][Ee][Ll][Oo][Ww][Ee][Rr]"/>
+ <xsd:pattern value="[Ss][Hh][Oo][Ww][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/>
+ <xsd:pattern value="[Ss][Hh][Oo][Ww][Mm][Ee][Nn][Uu]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Aa][Ll][Ww][Aa][Yy][Ss][Oo][Nn][Bb][Oo][Tt][Tt][Oo][Mm]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Aa][Ll][Ww][Aa][Yy][Ss][Oo][Nn][Tt][Oo][Pp]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Dd][Ee][Cc][Oo][Rr][Aa][Tt][Ii][Oo][Nn][Ss]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Dd][Oo][Cc][Kk][Aa][Uu][Tt][Oo][Hh][Ii][Dd][Ee]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Ff][Uu][Ll][Ll][Ss][Cc][Rr][Ee][Ee][Nn]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Ff][Uu][Ll][Ll]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Hh][Oo][Rr][Zz]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Vv][Ee][Rr][Tt]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Oo][Mm][Nn][Ii][Pp][Rr][Ee][Ss][Ee][Nn][Tt]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Ss][Hh][Aa][Dd][Ee]"/>
+ <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Ss][Hh][Oo][Ww][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/>
+ <xsd:pattern value="[Uu][Nn][Ff][Oo][Cc][Uu][Ss]"/>
+ <xsd:pattern value="[Uu][Nn][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Ff][Uu][Ll][Ll]"/>
+ <xsd:pattern value="[Uu][Nn][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Hh][Oo][Rr][Zz]"/>
+ <xsd:pattern value="[Uu][Nn][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Vv][Ee][Rr][Tt]"/>
+ <xsd:pattern value="[Uu][Nn][Ss][Hh][Aa][Dd][Ee]"/>
+ <xsd:pattern value="[Uu][Nn][Ss][Hh][Aa][Dd][Ee][Rr][Aa][Ii][Ss][Ee]"/>
+ <xsd:pattern value="[Uu][Nn][Ss][Hh][Oo][Ww][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="clienttype">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="desktop"/>
+ <xsd:enumeration value="dock"/>
+ <xsd:enumeration value="toolbar"/>
+ <xsd:enumeration value="menu"/>
+ <xsd:enumeration value="splash"/>
+ <xsd:enumeration value="utility"/>
+ <xsd:enumeration value="dialog"/>
+ <xsd:enumeration value="normal"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="bool">
+ <!-- this is copied to maximization. Keep that in sync. -->
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="yes"/>
+ <xsd:enumeration value="no"/>
+ <xsd:enumeration value="true"/>
+ <xsd:enumeration value="false"/>
+ <xsd:enumeration value="on"/>
+ <xsd:enumeration value="off"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="fontplace">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="ActiveWindow"/>
+ <xsd:enumeration value="InactiveWindow"/>
+ <xsd:enumeration value="MenuHeader"/>
+ <xsd:enumeration value="MenuItem"/>
+ <xsd:enumeration value="OnScreenDisplay"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="fontweight">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="normal"/>
+ <xsd:enumeration value="bold"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="fontslant">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="normal"/>
+ <xsd:enumeration value="italic"/>
+ <xsd:enumeration value="opaque"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="button">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="(([ACMSW]|Mod[1-5])-){,5}(Left|Middle|Right|Up|Down|Button[0-9]+)"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="center_or_int">
+ <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:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="mouse_or_int">
+ <xsd:restriction base="xsd:string">
+ <!-- ob: atoi($_) unless $_ eq 'center'; -->
+ <!-- I think the regexp DTRT WRT atoi. -->
+ <xsd:pattern value="mouse|0|[1-9][0-9]*"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="contextname">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="Desktop"/>
+ <xsd:enumeration value="Root"/>
+ <xsd:enumeration value="Client"/>
+ <xsd:enumeration value="Titlebar"/>
+ <xsd:enumeration value="Frame"/>
+ <xsd:enumeration value="TLCorner"/>
+ <xsd:enumeration value="TRCorner"/>
+ <xsd:enumeration value="BLCorner"/>
+ <xsd:enumeration value="BRCorner"/>
+ <xsd:enumeration value="Top"/>
+ <xsd:enumeration value="Left"/>
+ <xsd:enumeration value="Right"/>
+ <xsd:enumeration value="Bottom"/>
+ <xsd:enumeration value="Maximize"/>
+ <xsd:enumeration value="AllDesktops"/>
+ <xsd:enumeration value="Shade"/>
+ <xsd:enumeration value="Iconify"/>
+ <xsd:enumeration value="Icon"/>
+ <xsd:enumeration value="Close"/>
+ <xsd:enumeration value="MoveResize"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="direction">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="Horizontal"/>
+ <xsd:enumeration value="Vertical"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="dock_position">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="TopLeft"/>
+ <xsd:enumeration value="Top"/>
+ <xsd:enumeration value="TopRight"/>
+ <xsd:enumeration value="Right"/>
+ <xsd:enumeration value="BottomRight"/>
+ <xsd:enumeration value="Bottom"/>
+ <xsd:enumeration value="BottomLeft"/>
+ <xsd:enumeration value="Left"/>
+ <xsd:enumeration value="Floating"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="keyname">
+ <xsd:restriction base="xsd:string">
+ <!-- FIXME: M, Mod2, Mod5 in addition to S, A, C -->
+ <!-- how do we do all substrings and permutations? -->
+ <xsd:pattern value="(([ACMSW]|Mod[1-5])-){,5}[a-zA-Z0-9]*"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="layer">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="[Aa][Bb][Oo][Vv][Ee]"/>
+ <xsd:pattern value="[Nn][Oo][Rr][Mm][Aa][Ll]"/>
+ <xsd:pattern value="[Bb][Ee][Ll][Oo][Ww]"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="maximization">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="Horizontal"/>
+ <xsd:enumeration value="Vertical"/>
+ <!-- this is a copy of ob:bool. Keep it in sync. -->
+ <xsd:enumeration value="yes"/>
+ <xsd:enumeration value="no"/>
+ <xsd:enumeration value="true"/>
+ <xsd:enumeration value="false"/>
+ <xsd:enumeration value="on"/>
+ <xsd:enumeration value="off"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="mouseaction">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="Click"/>
+ <xsd:enumeration value="DoubleClick"/>
+ <xsd:enumeration value="Drag"/>
+ <xsd:enumeration value="Press"/>
+ <xsd:enumeration value="Release"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="placementpolicy">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="Smart"/>
+ <xsd:enumeration value="UnderMouse"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="placementmonitor">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="Any"/>
+ <xsd:enumeration value="Mouse"/>
+ <xsd:enumeration value="Active"/>
+ <xsd:enumeration value="Primary"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="primarymonitor">
+ <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:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="popupposition">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="Top"/>
+ <xsd:enumeration value="Center"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="popupshow">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="Always"/>
+ <xsd:enumeration value="Never"/>
+ <xsd:enumeration value="Nonpixel"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="dialogtype">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="None"/>
+ <xsd:enumeration value="Icons"/>
+ <xsd:enumeration value="List"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+</xsd:schema>
diff --git a/data/xbm/bullet.xbm b/data/xbm/bullet.xbm
new file mode 100644
index 0000000..88481ec
--- /dev/null
+++ b/data/xbm/bullet.xbm
@@ -0,0 +1,4 @@
+#define bullet_width 4
+#define bullet_height 7
+static unsigned char bullet_bits[] = {
+ 0x01, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x01 };
diff --git a/data/xbm/close.xbm b/data/xbm/close.xbm
new file mode 100644
index 0000000..4a88cff
--- /dev/null
+++ b/data/xbm/close.xbm
@@ -0,0 +1,4 @@
+#define close_width 6
+#define close_height 6
+static unsigned char close_bits[] = {
+ 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 };
diff --git a/data/xbm/desk.xbm b/data/xbm/desk.xbm
new file mode 100644
index 0000000..3e327e3
--- /dev/null
+++ b/data/xbm/desk.xbm
@@ -0,0 +1,4 @@
+#define desk_width 6
+#define desk_height 6
+static unsigned char desk_bits[] = {
+ 0x33, 0x33, 0x00, 0x00, 0x33, 0x33 };
diff --git a/data/xbm/desk_toggled.xbm b/data/xbm/desk_toggled.xbm
new file mode 100644
index 0000000..d7e045e
--- /dev/null
+++ b/data/xbm/desk_toggled.xbm
@@ -0,0 +1,4 @@
+#define desk_toggle_width 6
+#define desk_toggle_height 6
+static unsigned char desk_toggle_bits[] = {
+ 0x00, 0x1e, 0x1a, 0x16, 0x1e, 0x00 };
diff --git a/data/xbm/iconify.xbm b/data/xbm/iconify.xbm
new file mode 100644
index 0000000..2304866
--- /dev/null
+++ b/data/xbm/iconify.xbm
@@ -0,0 +1,4 @@
+#define iconify_width 6
+#define iconify_height 6
+static unsigned char iconify_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f };
diff --git a/data/xbm/max.xbm b/data/xbm/max.xbm
new file mode 100644
index 0000000..6d030af
--- /dev/null
+++ b/data/xbm/max.xbm
@@ -0,0 +1,4 @@
+#define max_width 6
+#define max_height 6
+static unsigned char max_bits[] = {
+ 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f };
diff --git a/data/xbm/max_toggled.xbm b/data/xbm/max_toggled.xbm
new file mode 100644
index 0000000..44c7cef
--- /dev/null
+++ b/data/xbm/max_toggled.xbm
@@ -0,0 +1,4 @@
+#define max_width 6
+#define max_height 6
+static unsigned char max_bits[] = {
+ 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f };
diff --git a/data/xbm/shade.xbm b/data/xbm/shade.xbm
new file mode 100644
index 0000000..edb3b17
--- /dev/null
+++ b/data/xbm/shade.xbm
@@ -0,0 +1,4 @@
+#define iconify_width 6
+#define iconify_height 6
+static unsigned char iconify_bits[] = {
+ 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 };
diff --git a/data/xbm/shade_toggled.xbm b/data/xbm/shade_toggled.xbm
new file mode 100644
index 0000000..edb3b17
--- /dev/null
+++ b/data/xbm/shade_toggled.xbm
@@ -0,0 +1,4 @@
+#define iconify_width 6
+#define iconify_height 6
+static unsigned char iconify_bits[] = {
+ 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 };
diff --git a/data/xsession/Makefile b/data/xsession/Makefile
new file mode 100644
index 0000000..b90edac
--- /dev/null
+++ b/data/xsession/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/data/xsession/openbox-gnome-session.in b/data/xsession/openbox-gnome-session.in
new file mode 100644
index 0000000..f31c9ad
--- /dev/null
+++ b/data/xsession/openbox-gnome-session.in
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+if test -n "$1"; then
+ echo "Syntax: openbox-gnome-session"
+ echo
+ echo "See the openbox-gnome-session(1) manpage for help."
+ exit
+fi
+
+# Clean up after GDM
+xprop -root -remove _NET_NUMBER_OF_DESKTOPS \
+ -remove _NET_DESKTOP_NAMES \
+ -remove _NET_CURRENT_DESKTOP 2> /dev/null
+
+VER=$(gnome-session --version 2>/dev/null | \
+ sed -e 's/[^0-9.]*\([0-9.]\+\)/\1/')
+
+MAJOR=$(echo $VER | cut -d . -f 1)
+MINOR=$(echo $VER | cut -d . -f 2)
+
+# run GNOME with Openbox as its window manager
+
+if test $MAJOR -lt 2 || (test $MAJOR = 2 && test $MINOR -le 22); then
+ # older gnome-session was easy to work with
+ export WINDOW_MANAGER="@bindir@/openbox"
+ exec gnome-session --choose-session=openbox-session "$@"
+elif test $MAJOR -lt 3; then
+ # old gnome-session requires openbox to be set in gconf and an
+ # openbox.desktop to be installed in the applications directory
+
+ SPATH=/desktop/gnome/session
+
+ # get the current default session
+ SESSION=$(gconftool-2 -g $SPATH/default_session 2> /dev/null)
+
+ # make sure openbox is going to be run
+ if test -z "$SESSION"; then
+ # if its empty then just run openbox
+ SESSION="[openbox]"
+ elif ! echo "$SESSION" | grep -q openbox; then
+ # if openbox isn't in the session then append it
+ SESSION="${SESSION%]},openbox]"
+ fi
+
+ # get the current GNOME/Openbox session
+ OB_SESSION=$(gconftool-2 -g $SPATH/openbox_session 2> /dev/null)
+
+ # update the GNOME/Openbox session if needed
+ if test x$OB_SESSION != x$SESSION; then
+ # the default session changed or we didn't run GNOME/Openbox before
+ gconftool-2 -t list --list-type=strings -s $SPATH/openbox_session \
+ "$SESSION" 2> /dev/null
+ fi
+
+ # run GNOME/Openbox
+ exec gnome-session --default-session-key $SPATH/openbox_session "$@"
+else
+ # new gnome-session requires session file installed in
+ # /usr/share/gnome-session/sessions as well as openbox.desktop to be
+ # installed in the applications directory
+
+ exec gnome-session --session=openbox-gnome
+fi
+
+
+
diff --git a/data/xsession/openbox-gnome.desktop.in b/data/xsession/openbox-gnome.desktop.in
new file mode 100644
index 0000000..19ae82e
--- /dev/null
+++ b/data/xsession/openbox-gnome.desktop.in
@@ -0,0 +1,8 @@
+[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
diff --git a/data/xsession/openbox-kde-session.in b/data/xsession/openbox-kde-session.in
new file mode 100644
index 0000000..3572279
--- /dev/null
+++ b/data/xsession/openbox-kde-session.in
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+if test -n "$1"; then
+ echo "Syntax: openbox-kde-session"
+ echo
+ echo "See the openbox-kde-session(1) manpage for help."
+ exit
+fi
+
+# Set the prefix for the menu layout to use
+export XDG_MENU_PREFIX="kde-4-"
+
+# Clean up after GDM
+xprop -root -remove _NET_NUMBER_OF_DESKTOPS \
+ -remove _NET_DESKTOP_NAMES \
+ -remove _NET_CURRENT_DESKTOP 2> /dev/null
+
+# Run KDE with Openbox as its window manager
+export KDEWM="@bindir@/openbox"
+exec startkde "$@"
diff --git a/data/xsession/openbox-kde.desktop.in b/data/xsession/openbox-kde.desktop.in
new file mode 100644
index 0000000..ddfc72d
--- /dev/null
+++ b/data/xsession/openbox-kde.desktop.in
@@ -0,0 +1,8 @@
+[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
diff --git a/data/xsession/openbox-session.in b/data/xsession/openbox-session.in
new file mode 100644
index 0000000..3cf3571
--- /dev/null
+++ b/data/xsession/openbox-session.in
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+if test -n "$1"; then
+ echo "Syntax: openbox-session"
+ echo
+ echo "See the openbox-session(1) manpage for help."
+ exit
+fi
+
+# Clean up after GDM
+xprop -root -remove _NET_NUMBER_OF_DESKTOPS \
+ -remove _NET_DESKTOP_NAMES \
+ -remove _NET_CURRENT_DESKTOP 2> /dev/null
+
+# Set up the environment
+A="@configdir@/openbox/environment"
+test -r $A && . $A
+A="${XDG_CONFIG_HOME:-"$HOME/.config"}/openbox/environment"
+test -r $A && . $A
+
+# Run Openbox, and have it run the autostart stuff
+exec @bindir@/openbox --startup "@libexecdir@/openbox-autostart OPENBOX" "$@"
diff --git a/data/xsession/openbox.desktop.in b/data/xsession/openbox.desktop.in
new file mode 100644
index 0000000..0914e5b
--- /dev/null
+++ b/data/xsession/openbox.desktop.in
@@ -0,0 +1,8 @@
+[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
diff --git a/debian/changelog.in b/debian/changelog.in
new file mode 100644
index 0000000..a217280
--- /dev/null
+++ b/debian/changelog.in
@@ -0,0 +1,6 @@
+openbox (@version@) unstable; urgency=low
+
+ * Upstream release.
+
+ -- Dana Jansens <danakj@orodu.net> @time@
+
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+5
diff --git a/debian/conffiles b/debian/conffiles
new file mode 100644
index 0000000..6319a21
--- /dev/null
+++ b/debian/conffiles
@@ -0,0 +1,2 @@
+/etc/xdg/openbox/rc.xml
+/etc/xdg/openbox/menu.xml
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..61f72cc
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,16 @@
+Source: openbox
+Section: x11
+Priority: optional
+Maintainer: Dana Jansens <danakj@orodu.net>
+Build-Depends: debhelper (>= 5), coreutils, sed, autotools-dev, gcc, automake1.9, autoconf, libtool, pkg-config, libpango1.0-dev, libglib2.0-dev, libxml2-dev, libxcursor-dev, libstartup-notification0-dev, xlibs-dev, libxinerama-dev, libxft2-dev
+Standards-Version: 3.7.2
+
+Package: openbox
+Architecture: i386
+Depends: libc6, libglib2.0-0, libxcursor1, libx11-6, libpango1.0-0, libice6, libsm6, libxext6, libxinerama1, libxml2, libstartup-notification0, libxft2
+Recommends: obconf, ttf-bitstream-vera
+Suggests: menu, x-display-manager, ksmserver | gnome-session, kdm | gdm
+Conflicts: menu (<< 2.1.12)
+Provides: x-window-manager
+Description: A minimalistic, highly configurable, next generation window manager with extensive standards support.
+ http://openbox.org/
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..6c93400
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,21 @@
+Homepage: http://openbox.org/
+
+The main upstream author is Dana Jansens <danakj@orodu.net>. To see
+a more complete listing of contributors, look at the AUTHORS file.
+
+Copyright:
+
+ 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, or (at your option) any
+ later version.
+
+ On Debian GNU/Linux systems, the complete text of the GNU General
+ Public License can be found in `/usr/share/common-licenses/GPL'.
+
+Copyright for the Debian-related build-scripts:
+
+ Copyright (C) 2007 Dana Jansens <danakj@orodu.net>
+
+ These scripts are placed under the BSD license, which may be found in
+ the file `/usr/share/common-licenses/BSD' on most Debian systems.
diff --git a/debian/menu b/debian/menu
new file mode 100644
index 0000000..97d2d14
--- /dev/null
+++ b/debian/menu
@@ -0,0 +1,6 @@
+?package(openbox):\
+ needs="wm"\
+ section="WindowManagers"\
+ title="Openbox"\
+ command="/usr/bin/openbox"\
+ icon="/usr/share/pixmaps/openbox.png"
diff --git a/debian/postinst b/debian/postinst
new file mode 100644
index 0000000..aa69037
--- /dev/null
+++ b/debian/postinst
@@ -0,0 +1,42 @@
+#!/bin/sh
+# postinst script for openbox
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ configure)
+ if [ -x /usr/bin/update-menus ]; then update-menus; fi
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/postrm b/debian/postrm
new file mode 100644
index 0000000..641c6b9
--- /dev/null
+++ b/debian/postrm
@@ -0,0 +1,43 @@
+#!/bin/sh
+# postrm script for openbox
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <overwriter>
+# <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+case "$1" in
+ purge)
+ ;;
+ remove)
+ if [ -x /usr/bin/update-menus ]; then update-menus ; fi
+ ;;
+ upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..dea855b
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,107 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+
+CFLAGS = -g
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+ CFLAGS += -O0
+else
+ CFLAGS += -O2
+endif
+
+config.status: configure
+ dh_testdir
+ # Add here commands to configure the package.
+ ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --sysconfdir=/etc --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info CFLAGS="$(CFLAGS)" LDFLAGS="-Wl,-z,defs"
+
+
+build: build-stamp
+
+build-stamp: config.status
+ dh_testdir
+
+ # Add here commands to compile the package.
+ $(MAKE)
+ #docbook-to-man debian/openbox.sgml > openbox.1
+
+ touch $@
+
+clean:
+ dh_testdir
+ dh_testroot
+ rm -f build-stamp
+
+ # Add here commands to clean up after the build process.
+ -$(MAKE) distclean
+ifneq "$(wildcard /usr/share/misc/config.sub)" ""
+ cp -f /usr/share/misc/config.sub config.sub
+endif
+ifneq "$(wildcard /usr/share/misc/config.guess)" ""
+ cp -f /usr/share/misc/config.guess config.guess
+endif
+
+
+ dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+
+ # Add here commands to install the package into debian/openbox.
+ $(MAKE) DESTDIR=$(CURDIR)/debian/openbox install
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs CHANGELOG
+# dh_installdocs
+# dh_installexamples
+# dh_install
+ dh_installmenu
+# dh_installdebconf
+# dh_installlogrotate
+# dh_installemacsen
+# dh_installpam
+# dh_installmime
+# dh_python
+# dh_installinit
+# dh_installcron
+# dh_installinfo
+# dh_installman
+ dh_link
+ dh_strip
+ dh_compress
+ dh_fixperms
+# dh_perl
+# dh_makeshlibs
+ dh_installdeb
+ dh_shlibdeps
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..b90edac
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/doc/doxygen/comments b/doc/doxygen/comments
new file mode 100644
index 0000000..7930f36
--- /dev/null
+++ b/doc/doxygen/comments
@@ -0,0 +1,11 @@
+// Further comments for doxygen on the src/ directory
+
+/*! @namespace ob
+ The namespace containing the %Openbox window manager application
+*/
+
+/*! @namespace otk
+ The namespace containing a toolkit used by the Openbox window manager,
+ and also for use by external applications that wish to present a similar
+ visual appearance to Openbox.
+*/
diff --git a/doc/doxygen/doxygen.conf.in b/doc/doxygen/doxygen.conf.in
new file mode 100644
index 0000000..768dc19
--- /dev/null
+++ b/doc/doxygen/doxygen.conf.in
@@ -0,0 +1,192 @@
+# Doxyfile 1.2.18
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME = Openbox
+PROJECT_NUMBER = @version@
+OUTPUT_DIRECTORY =
+OUTPUT_LANGUAGE = English
+EXTRACT_ALL = YES
+EXTRACT_PRIVATE = YES
+EXTRACT_STATIC = YES
+EXTRACT_LOCAL_CLASSES = YES
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH = @basedir@
+INTERNAL_DOCS = NO
+STRIP_CODE_COMMENTS = NO
+CASE_SENSE_NAMES = NO
+SHORT_NAMES = NO
+HIDE_SCOPE_NAMES = NO
+VERBATIM_HEADERS = YES
+SHOW_INCLUDE_FILES = YES
+JAVADOC_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = YES
+DETAILS_AT_TOP = YES
+INHERIT_DOCS = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 2
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+ALIASES =
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+OPTIMIZE_OUTPUT_FOR_C = NO
+OPTIMIZE_OUTPUT_JAVA = NO
+SHOW_USED_FILES = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = YES
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = ../../src ../../otk comments
+FILE_PATTERNS = *.hh *.cc
+RECURSIVE = NO
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS = *_wrap.cc
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = YES
+INLINE_SOURCES = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = NO
+COLS_IN_ALPHA_INDEX = 5
+IGNORE_PREFIX = OB OTK _
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 200
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = YES
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+COMPACT_LATEX = NO
+PAPER_TYPE = letter
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = YES
+USE_PDFLATEX = NO
+LATEX_BATCHMODE = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = NO
+XML_SCHEMA =
+XML_DTD =
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF = NO
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED = DOXYGEN_IGNORE
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+HIDE_UNDOC_RELATIONS = YES
+HAVE_DOT = YES
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+TEMPLATE_RELATIONS = YES
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+GRAPHICAL_HIERARCHY = NO
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
+CGI_NAME = search.cgi
+CGI_URL =
+DOC_URL =
+DOC_ABSPATH =
+BIN_ABSPATH = /usr/local/bin/
+EXT_DOC_PATHS =
diff --git a/doc/obxprop.1.in b/doc/obxprop.1.in
new file mode 100644
index 0000000..90f01ae
--- /dev/null
+++ b/doc/obxprop.1.in
@@ -0,0 +1,46 @@
+.TH "OBXPROP" "1"
+.SH "NAME"
+openbox \(em A minimalistic, highly configurable, next generation window
+manager with extensive standards support.
+.SH "SYNOPSIS"
+.PP
+\fBopenbox\fR [\-\-help] [\-\-display DISPLAY] [\-\-id ID] [\-\-root] [\-\-] [PROPERTY \&...]
+.SH "DESCRIPTION"
+.PP
+obxprop is a tool for displaying the properties on an x
+window.
+.PP
+This tool has a similar functionality to \fBxprop\fR,
+but obxprop allows you to see UTF-8 strings as text.
+.PP
+You may use the \fB\-\-id\fR option to specify a window
+identifier, otherwise obxprop will allow you to select a window by
+clicking on it.
+.PP
+Primarily, this tool exists for Openbox users to see the value of the
+_OB_APP_NAME, _OB_APP_CLASS, _OB_APP_ROLE, and _OB_APP_TYPE properties,
+which Openbox uses for
+matching windows against user-defined application rules.
+.SH "OPTIONS"
+.PP
+These are the possible options that \fBopenbox\fR accepts:
+.IP "\fB\-\-help\fP" 10
+Show a summary of the options available.
+.IP "\fB\-\-display\fP DISPLAY" 10
+Specify the X display to use.
+.IP "\fB\-\-id\fP ID" 10
+Specify the window identifier for the window whose properties
+will be displayed.
+.PP
+Similar to \fBxprop\fR,
+you may pass one or more property names to have
+\fBopenbox\fR limit its output to only the properties
+requested.
+.SH "SEE ALSO"
+.PP
+openbox (1), openbox-session(1), openbox-gnome-session(1),
+openbox-kde-session(1).
+.PP
+Please report bugs to: \fBhttp://bugzilla.icculus.org/
+\fP
+.\" created by instant / docbook-to-man, Mon 08 Feb 2010, 15:03
diff --git a/doc/obxprop.1.sgml b/doc/obxprop.1.sgml
new file mode 100644
index 0000000..bf89442
--- /dev/null
+++ b/doc/obxprop.1.sgml
@@ -0,0 +1,124 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
+
+<!-- Process this file with docbook-to-man to generate an nroff manual
+ page: `docbook-to-man manpage.sgml > manpage.1'. You may view
+ the manual page with: `docbook-to-man manpage.sgml | nroff -man |
+ less'. A typical entry in a Makefile or Makefile.am is:
+
+manpage.1: manpage.sgml
+ docbook-to-man $< > $@
+
+
+ The docbook-to-man binary is found in the docbook-to-man package.
+ Please remember that if you create the nroff version in one of the
+ debian/rules file targets (such as build), you will need to include
+ docbook-to-man in your Build-Depends control field.
+
+ -->
+
+ <!ENTITY dhsection "<manvolnum>1</manvolnum>">
+ <!ENTITY dhucpackage "<refentrytitle>OBXPROP</refentrytitle>">
+ <!ENTITY dhpackage "openbox">
+]>
+
+<refentry>
+ <refmeta>
+ &dhucpackage;
+
+ &dhsection;
+ </refmeta>
+ <refnamediv>
+ <refname>&dhpackage;</refname>
+
+ <refpurpose>A minimalistic, highly configurable, next generation window
+ manager with extensive standards support.</refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>&dhpackage;</command>
+ <arg choice="Opt">--help</arg>
+ <arg choice="Opt">--display DISPLAY</arg>
+ <arg choice="Opt">--id ID</arg>
+ <arg choice="Opt">--root</arg>
+ <arg choice="Opt">--</arg>
+ <arg choice="Opt" rep="Repeat">PROPERTY</arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+ <refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>obxprop is a tool for displaying the properties on an x
+ window.</para>
+
+ <para>This tool has a similar functionality to <command>xprop</command>,
+ but obxprop allows you to see UTF-8 strings as text.</para>
+
+ <para>You may use the <command>--id</command> option to specify a window
+ identifier, otherwise obxprop will allow you to select a window by
+ clicking on it.</para>
+
+ <para>Primarily, this tool exists for Openbox users to see the value of the
+ _OB_APP_NAME, _OB_APP_CLASS, _OB_APP_ROLE, and _OB_APP_TYPE properties,
+ which Openbox uses for
+ matching windows against user-defined application rules.</para>
+ </refsect1>
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <para>These are the possible options that <command>&dhpackage;</command> accepts:</para>
+ <variablelist>
+ <varlistentry>
+ <term><option>--help</option></term>
+ <listitem>
+ <para>Show a summary of the options available.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>--display</option> DISPLAY</term>
+ <listitem>
+ <para>Specify the X display to use.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>--id</option> ID</term>
+ <listitem>
+ <para>Specify the window identifier for the window whose properties
+ will be displayed.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>Similar to <command>xprop</command>,
+ you may pass one or more property names to have
+ <command>&dhpackage;</command> limit its output to only the properties
+ requested.</para>
+ </refsect1>
+ <refsect1>
+ <title>SEE ALSO</title>
+
+ <para>openbox (1), openbox-session(1), openbox-gnome-session(1),
+ openbox-kde-session(1).</para>
+
+ <para>Please report bugs to: <literal>http://bugzilla.icculus.org/
+ </literal></para>
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:2
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:nil
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+-->
+
+
diff --git a/doc/openbox-gnome-session.1.in b/doc/openbox-gnome-session.1.in
new file mode 100644
index 0000000..7256874
--- /dev/null
+++ b/doc/openbox-gnome-session.1.in
@@ -0,0 +1,22 @@
+.TH "OPENBOX" "1"
+.SH "NAME"
+openbox-gnome-session \(em Run a GNOME session with Openbox as the window manager
+
+.SH "DESCRIPTION"
+.PP
+\fBopenbox-gnome-session\fR runs a GNOME session with
+Openbox as the window manager.
+.PP
+\fBopenbox-gnome-session\fR does not take any command line
+arguments.
+.SH "SEE ALSO"
+.PP
+openbox(1), openbox-kde-session(1), openbox-session(1).
+
+.PP
+The program's full documentation is available on the website:
+\fBhttp://openbox.org/\fP
+.PP
+Please report bugs to: \fBhttp://bugzilla.icculus.org/
+\fP
+.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40
diff --git a/doc/openbox-gnome-session.1.sgml b/doc/openbox-gnome-session.1.sgml
new file mode 100644
index 0000000..66add1b
--- /dev/null
+++ b/doc/openbox-gnome-session.1.sgml
@@ -0,0 +1,76 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
+
+<!-- Process this file with docbook-to-man to generate an nroff manual
+ page: `docbook-to-man manpage.sgml > manpage.1'. You may view
+ the manual page with: `docbook-to-man manpage.sgml | nroff -man |
+ less'. A typical entry in a Makefile or Makefile.am is:
+
+manpage.1: manpage.sgml
+ docbook-to-man $< > $@
+
+
+ The docbook-to-man binary is found in the docbook-to-man package.
+ Please remember that if you create the nroff version in one of the
+ debian/rules file targets (such as build), you will need to include
+ docbook-to-man in your Build-Depends control field.
+
+ -->
+
+ <!ENTITY dhsection "<manvolnum>1</manvolnum>">
+ <!ENTITY dhucpackage "<refentrytitle>OPENBOX</refentrytitle>">
+ <!ENTITY dhpackage "openbox-gnome-session">
+]>
+
+<refentry>
+ <refmeta>
+ &dhucpackage;
+
+ &dhsection;
+ </refmeta>
+ <refnamediv>
+ <refname>&dhpackage;</refname>
+
+ <refpurpose>Run a GNOME session with Openbox as the window manager
+ </refpurpose>
+ </refnamediv>
+ <refsect1>
+ <title>DESCRIPTION</title>
+
+ <para><command>&dhpackage;</command> runs a GNOME session with
+ Openbox as the window manager.</para>
+
+ <para><command>&dhpackage;</command> does not take any command line
+ arguments.</para>
+ </refsect1>
+ <refsect1>
+ <title>SEE ALSO</title>
+
+ <para>openbox(1), openbox-kde-session(1), openbox-session(1).
+ </para>
+
+ <para>The program's full documentation is available on the website:
+ <literal>http://openbox.org/</literal></para>
+
+ <para>Please report bugs to: <literal>http://bugzilla.icculus.org/
+ </literal></para>
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:2
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:nil
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+-->
+
+
diff --git a/doc/openbox-kde-session.1.in b/doc/openbox-kde-session.1.in
new file mode 100644
index 0000000..9a96275
--- /dev/null
+++ b/doc/openbox-kde-session.1.in
@@ -0,0 +1,22 @@
+.TH "OPENBOX" "1"
+.SH "NAME"
+openbox-kde-session \(em Run a KDE session with Openbox as the window manager
+
+.SH "DESCRIPTION"
+.PP
+\fBopenbox-kde-session\fR runs a KDE session with
+Openbox as the window manager.
+.PP
+\fBopenbox-kde-session\fR does not take any command line
+arguments.
+.SH "SEE ALSO"
+.PP
+openbox(1), openbox-gnome-session(1), openbox-session(1).
+
+.PP
+The program's full documentation is available on the website:
+\fBhttp://openbox.org/\fP
+.PP
+Please report bugs to: \fBhttp://bugzilla.icculus.org/
+\fP
+.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40
diff --git a/doc/openbox-kde-session.1.sgml b/doc/openbox-kde-session.1.sgml
new file mode 100644
index 0000000..70c5117
--- /dev/null
+++ b/doc/openbox-kde-session.1.sgml
@@ -0,0 +1,76 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
+
+<!-- Process this file with docbook-to-man to generate an nroff manual
+ page: `docbook-to-man manpage.sgml > manpage.1'. You may view
+ the manual page with: `docbook-to-man manpage.sgml | nroff -man |
+ less'. A typical entry in a Makefile or Makefile.am is:
+
+manpage.1: manpage.sgml
+ docbook-to-man $< > $@
+
+
+ The docbook-to-man binary is found in the docbook-to-man package.
+ Please remember that if you create the nroff version in one of the
+ debian/rules file targets (such as build), you will need to include
+ docbook-to-man in your Build-Depends control field.
+
+ -->
+
+ <!ENTITY dhsection "<manvolnum>1</manvolnum>">
+ <!ENTITY dhucpackage "<refentrytitle>OPENBOX</refentrytitle>">
+ <!ENTITY dhpackage "openbox-kde-session">
+]>
+
+<refentry>
+ <refmeta>
+ &dhucpackage;
+
+ &dhsection;
+ </refmeta>
+ <refnamediv>
+ <refname>&dhpackage;</refname>
+
+ <refpurpose>Run a KDE session with Openbox as the window manager
+ </refpurpose>
+ </refnamediv>
+ <refsect1>
+ <title>DESCRIPTION</title>
+
+ <para><command>&dhpackage;</command> runs a KDE session with
+ Openbox as the window manager.</para>
+
+ <para><command>&dhpackage;</command> does not take any command line
+ arguments.</para>
+ </refsect1>
+ <refsect1>
+ <title>SEE ALSO</title>
+
+ <para>openbox(1), openbox-gnome-session(1), openbox-session(1).
+ </para>
+
+ <para>The program's full documentation is available on the website:
+ <literal>http://openbox.org/</literal></para>
+
+ <para>Please report bugs to: <literal>http://bugzilla.icculus.org/
+ </literal></para>
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:2
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:nil
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+-->
+
+
diff --git a/doc/openbox-session.1.in b/doc/openbox-session.1.in
new file mode 100644
index 0000000..483cdfd
--- /dev/null
+++ b/doc/openbox-session.1.in
@@ -0,0 +1,42 @@
+.TH "OPENBOX" "1"
+.SH "NAME"
+openbox-session \(em Runs an Openbox session without any session manager.
+
+.SH "DESCRIPTION"
+.PP
+\fBopenbox-session\fR runs an openbox session without
+any session manager. Without a session manager, you will not be able
+to save your state from one log in to the next.
+.PP
+\fBopenbox-session\fR does not take any command line
+arguments.
+.PP
+On log in, \fBopenbox-session\fR will run the
+~/.config/openbox/autostart.sh script if it exists, and will run the
+system-wide script @configdir@/openbox/autostart.sh otherwise. You may
+place anything you want to run automatically in those files, for example:
+
+.PP
+.RS
+.PP
+.nf
+xsetroot \-solid grey &
+gnome-settings-daemon &
+.fi
+.RE
+.PP
+Make sure that each line is followed by a "&" or else the script will
+stop there and further commands will not be executed. You can use the
+@configdir@/openbox/autostart.sh file as an example for creating your
+own.
+.SH "SEE ALSO"
+.PP
+openbox(1), openbox-session(1), openbox-gnome-session(1).
+
+.PP
+The program's full documentation is available on the website:
+\fBhttp://openbox.org/\fP
+.PP
+Please report bugs to: \fBhttp://bugzilla.icculus.org/
+\fP
+.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40
diff --git a/doc/openbox-session.1.sgml b/doc/openbox-session.1.sgml
new file mode 100644
index 0000000..ccece7a
--- /dev/null
+++ b/doc/openbox-session.1.sgml
@@ -0,0 +1,93 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
+
+<!-- Process this file with docbook-to-man to generate an nroff manual
+ page: `docbook-to-man manpage.sgml > manpage.1'. You may view
+ the manual page with: `docbook-to-man manpage.sgml | nroff -man |
+ less'. A typical entry in a Makefile or Makefile.am is:
+
+manpage.1: manpage.sgml
+ docbook-to-man $< > $@
+
+
+ The docbook-to-man binary is found in the docbook-to-man package.
+ Please remember that if you create the nroff version in one of the
+ debian/rules file targets (such as build), you will need to include
+ docbook-to-man in your Build-Depends control field.
+
+ -->
+
+ <!ENTITY dhsection "<manvolnum>1</manvolnum>">
+ <!ENTITY dhucpackage "<refentrytitle>OPENBOX</refentrytitle>">
+ <!ENTITY dhpackage "openbox-session">
+]>
+
+<refentry>
+ <refmeta>
+ &dhucpackage;
+
+ &dhsection;
+ </refmeta>
+ <refnamediv>
+ <refname>&dhpackage;</refname>
+
+ <refpurpose>Runs an Openbox session without any session manager.
+ </refpurpose>
+ </refnamediv>
+ <refsect1>
+ <title>DESCRIPTION</title>
+
+ <para><command>&dhpackage;</command> runs an openbox session without
+ any session manager. Without a session manager, you will not be able
+ to save your state from one log in to the next.</para>
+
+ <para><command>&dhpackage;</command> does not take any command line
+ arguments.</para>
+
+ <para>On log in, <command>&dhpackage;</command> will run the
+ ~/.config/openbox/autostart.sh script if it exists, and will run the
+ system-wide script @configdir@/openbox/autostart.sh otherwise. You may
+ place anything you want to run automatically in those files, for example:
+ </para>
+
+ <blockquote><literallayout>
+ xsetroot -solid grey &
+ gnome-settings-daemon &</literallayout></blockquote>
+
+ <para>Make sure that each line is followed by a "&" or else the script will
+ stop there and further commands will not be executed. You can use the
+ @configdir@/openbox/autostart.sh file as an example for creating your
+ own.</para>
+
+ </refsect1>
+ <refsect1>
+ <title>SEE ALSO</title>
+
+ <para>openbox(1), openbox-session(1), openbox-gnome-session(1).
+ </para>
+
+ <para>The program's full documentation is available on the website:
+ <literal>http://openbox.org/</literal></para>
+
+ <para>Please report bugs to: <literal>http://bugzilla.icculus.org/
+ </literal></para>
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:2
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:nil
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+-->
+
+
diff --git a/doc/openbox.1.in b/doc/openbox.1.in
new file mode 100644
index 0000000..d508e8c
--- /dev/null
+++ b/doc/openbox.1.in
@@ -0,0 +1,95 @@
+.TH "OPENBOX" "1"
+.SH "NAME"
+openbox \(em A minimalistic, highly configurable, next generation window
+manager with extensive standards support.
+.SH "SYNOPSIS"
+.PP
+\fBopenbox\fR [\fB\-\-help\fP] [\fB\-\-version\fP] [\fB\-\-replace\fP] [\fB\-\-reconfigure\fP] [\fB\-\-restart\fP] [\fB\-\-sm-disable\fP] [\fB\-\-sync\fP] [\fB\-\-debug\fP] [\fB\-\-debug-focus\fP] [\fB\-\-debug-xinerama\fP]
+.SH "DESCRIPTION"
+.PP
+Openbox is minimalistic, highly configurable, next generation window
+manager with extensive standards support.
+.PP
+You can start Openbox in three ways:
+.PP
+If you run a display manager such as GDM, you will find 3 entries
+in the login session type menu for Openbox:
+\fBGNOME/Openbox\fR, \fBKDE/Openbox\fR and \fBOpenbox\fR. If you want to use Openbox
+within GNOME or KDE, you can choose the appropriate entry, and it will
+launch GNOME or KDE with Openbox as the window manager.
+.PP
+The third option at log in, which is \fBOpenbox\fR without a session manager, uses the \fBopenbox-session\fR command to start Openbox. On log in, \fBopenbox\fR will
+run the ~/.config/openbox/autostart.sh script if it exists, and will run
+the system-wide script @configdir@/openbox/autostart.sh otherwise. You
+may place anything you want to run automatically in those files, for
+example:
+
+.PP
+.RS
+.PP
+.nf
+xsetroot \-solid grey &
+gnome-settings-daemon &
+.fi
+.RE
+.PP
+Make sure that each line is followed by a "&" or else the script will
+stop there and further commands will not be executed. You can use the
+@configdir@/openbox/autostart.sh file as an example for creating your
+own.
+.PP
+The default @configdir@/openbox/autostart.sh runs a number of things
+with Openbox.
+.PP
+Lastly, if you use \fBstartx\fR to launch your X
+session, you can set up a ~/.xinitrc file to run
+\fBopenbox-session\fR and follow the same directions as
+above regarding the autostart.sh file.
+.PP
+You can use the \fBobconf\fR tool to configure Openbox
+easily with a graphical interface, however more in-depth configuration
+is possible by editing the configuration files by hand.
+.PP
+The default configuration and menu files are installed in
+@configdir@/openbox/, and the user configuration is placed in
+~/.config/openbox/. You can copy the default configuration and menus
+to ~/.config/openbox and edit it to your liking.
+.SH "OPTIONS"
+.PP
+These are the possible options that \fBopenbox\fR accepts:
+.IP "\fB\-\-help\fP" 10
+Show a summary of the options available.
+.IP "\fB\-\-version\fP" 10
+Show the version of the program.
+.IP "\fB\-\-replace\fP" 10
+Replace the currently running window manager.
+.IP "\fB\-\-reconfigure\fP" 10
+If Openbox is already running on the display, tell it to
+reload its configuration.
+.IP "\fB\-\-restart\fP" 10
+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\-\-sm-disable\fP" 10
+Do not connect to the session manager.
+.IP "\fB\-\-sync\fP" 10
+Run in synchronous mode (for debugging).
+.IP "\fB\-\-debug\fP" 10
+Display debugging output.
+.IP "\fB\-\-debug-focus\fP" 10
+Display debugging output for focus handling.
+.IP "\fB\-\-debug-xinerama\fP" 10
+Split the display into two fake xinerama regions, if
+xinerama is not already enabled. This is for debugging
+xinerama support.
+.SH "SEE ALSO"
+.PP
+obconf (1), openbox-session(1), openbox-gnome-session(1),
+openbox-kde-session(1).
+.PP
+The program's full documentation is available on the website:
+\fBhttp://openbox.org/\fP
+.PP
+Please report bugs to: \fBhttp://bugzilla.icculus.org/
+\fP
+.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40
diff --git a/doc/openbox.1.sgml b/doc/openbox.1.sgml
new file mode 100644
index 0000000..275c324
--- /dev/null
+++ b/doc/openbox.1.sgml
@@ -0,0 +1,204 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
+
+<!-- Process this file with docbook-to-man to generate an nroff manual
+ page: `docbook-to-man manpage.sgml > manpage.1'. You may view
+ the manual page with: `docbook-to-man manpage.sgml | nroff -man |
+ less'. A typical entry in a Makefile or Makefile.am is:
+
+manpage.1: manpage.sgml
+ docbook-to-man $< > $@
+
+
+ The docbook-to-man binary is found in the docbook-to-man package.
+ Please remember that if you create the nroff version in one of the
+ debian/rules file targets (such as build), you will need to include
+ docbook-to-man in your Build-Depends control field.
+
+ -->
+
+ <!ENTITY dhsection "<manvolnum>1</manvolnum>">
+ <!ENTITY dhucpackage "<refentrytitle>OPENBOX</refentrytitle>">
+ <!ENTITY dhpackage "openbox">
+]>
+
+<refentry>
+ <refmeta>
+ &dhucpackage;
+
+ &dhsection;
+ </refmeta>
+ <refnamediv>
+ <refname>&dhpackage;</refname>
+
+ <refpurpose>A minimalistic, highly configurable, next generation window
+ manager with extensive standards support.</refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>&dhpackage;</command>
+ <arg><option>--help</option></arg>
+ <arg><option>--version</option></arg>
+ <arg><option>--replace</option></arg>
+ <arg><option>--reconfigure</option></arg>
+ <arg><option>--restart</option></arg>
+ <arg><option>--sm-disable</option></arg>
+ <arg><option>--sync</option></arg>
+ <arg><option>--debug</option></arg>
+ <arg><option>--debug-focus</option></arg>
+ <arg><option>--debug-xinerama</option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+ <refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>Openbox is minimalistic, highly configurable, next generation window
+ manager with extensive standards support.</para>
+
+ <para>You can start Openbox in three ways:</para>
+
+ <para>If you run a display manager such as GDM, you will find 3 entries
+ in the login session type menu for Openbox:
+ <command>GNOME/Openbox</command>, <command>KDE/Openbox</command>
+ and <command>Openbox</command>. If you want to use Openbox
+ within GNOME or KDE, you can choose the appropriate entry, and it will
+ launch GNOME or KDE with Openbox as the window manager.</para>
+
+ <para>The third option at log in, which is <command>Openbox</command>
+ without a session manager, uses the <command>openbox-session</command>
+ command to start Openbox. On log in, <command>&dhpackage;</command> will
+ run the ~/.config/openbox/autostart.sh script if it exists, and will run
+ the system-wide script @configdir@/openbox/autostart.sh otherwise. You
+ may place anything you want to run automatically in those files, for
+ example:
+ </para>
+
+ <blockquote><literallayout>
+ xsetroot -solid grey &
+ gnome-settings-daemon &</literallayout></blockquote>
+
+ <para>Make sure that each line is followed by a "&" or else the script will
+ stop there and further commands will not be executed. You can use the
+ @configdir@/openbox/autostart.sh file as an example for creating your
+ own.</para>
+
+ <para>The default @configdir@/openbox/autostart.sh runs a number of things
+ with Openbox.</para>
+
+ <para>Lastly, if you use <command>startx</command> to launch your X
+ session, you can set up a ~/.xinitrc file to run
+ <command>openbox-session</command> and follow the same directions as
+ above regarding the autostart.sh file.</para>
+
+ <para>You can use the <command>obconf</command> tool to configure Openbox
+ easily with a graphical interface, however more in-depth configuration
+ is possible by editing the configuration files by hand.</para>
+
+ <para>The default configuration and menu files are installed in
+ @configdir@/openbox/, and the user configuration is placed in
+ ~/.config/openbox/. You can copy the default configuration and menus
+ to ~/.config/openbox and edit it to your liking.</para>
+ </refsect1>
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <para>These are the possible options that <command>&dhpackage;</command> accepts:</para>
+ <variablelist>
+ <varlistentry>
+ <term><option>--help</option></term>
+ <listitem>
+ <para>Show a summary of the options available.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>--version</option></term>
+ <listitem>
+ <para>Show the version of the program.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>--replace</option></term>
+ <listitem>
+ <para>Replace the currently running window manager.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>--reconfigure</option></term>
+ <listitem>
+ <para>If Openbox is already running on the display, tell it to
+ reload its configuration.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>--restart</option></term>
+ <listitem>
+ <para>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.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>--sm-disable</option></term>
+ <listitem>
+ <para>Do not connect to the session manager.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>--sync</option></term>
+ <listitem>
+ <para>Run in synchronous mode (for debugging).</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>--debug</option></term>
+ <listitem>
+ <para>Display debugging output.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>--debug-focus</option></term>
+ <listitem>
+ <para>Display debugging output for focus handling.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>--debug-xinerama</option></term>
+ <listitem>
+ <para>Split the display into two fake xinerama regions, if
+ xinerama is not already enabled. This is for debugging
+ xinerama support.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+ <refsect1>
+ <title>SEE ALSO</title>
+
+ <para>obconf (1), openbox-session(1), openbox-gnome-session(1),
+ openbox-kde-session(1).</para>
+
+ <para>The program's full documentation is available on the website:
+ <literal>http://openbox.org/</literal></para>
+
+ <para>Please report bugs to: <literal>http://bugzilla.icculus.org/
+ </literal></para>
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:2
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:nil
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+-->
+
+
diff --git a/doc/rc-mouse-focus.xml b/doc/rc-mouse-focus.xml
new file mode 100644
index 0000000..dc7f2e9
--- /dev/null
+++ b/doc/rc-mouse-focus.xml
@@ -0,0 +1,636 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Do not edit this file, it will be overwritten on install.
+ Copy the file to $HOME/.config/openbox/ instead. -->
+
+<openbox_config xmlns="http://openbox.org/3.4/rc"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<resistance>
+ <strength>10</strength>
+ <screen_edge_strength>20</screen_edge_strength>
+</resistance>
+
+<focus>
+ <focusNew>yes</focusNew>
+ <!-- always try to focus new windows when they appear. other rules do
+ apply -->
+ <followMouse>yes</followMouse>
+ <!-- move focus to a window when you move the mouse into it -->
+ <focusLast>no</focusLast>
+ <!-- focus the last used window when changing desktops, instead of the one
+ under the mouse pointer. when followMouse is enabled -->
+ <underMouse>yes</underMouse>
+ <!-- move focus under the mouse, even when the mouse is not moving -->
+ <focusDelay>200</focusDelay>
+ <!-- when followMouse is enabled, the mouse must be inside the window for
+ this many milliseconds (1000 = 1 sec) before moving focus to it -->
+ <raiseOnFocus>no</raiseOnFocus>
+ <!-- when followMouse is enabled, and a window is given focus by moving the
+ mouse into it, also raise the window -->
+</focus>
+
+<placement>
+ <policy>Smart</policy>
+ <!-- 'Smart' or 'UnderMouse' -->
+</placement>
+
+<theme>
+ <name>Clearlooks</name>
+ <titleLayout>NLIMC</titleLayout>
+ <!--
+ avaible characters are NDSLIMC, each can occur at most once.
+ N: window icon
+ L: window label (AKA title).
+ I: iconify
+ M: maximize
+ C: close
+ S: shade (roll up/down)
+ D: omnipresent (on all desktops).
+ -->
+ <keepBorder>yes</keepBorder>
+ <animateIconify>yes</animateIconify>
+ <font place="ActiveWindow">
+ <name>sans</name>
+ <size>8</size>
+ <!-- font size in points -->
+ <weight>bold</weight>
+ <!-- 'bold' or 'normal' -->
+ <slant>normal</slant>
+ <!-- 'italic' or 'normal' -->
+ </font>
+ <font place="InactiveWindow">
+ <name>sans</name>
+ <size>8</size>
+ <!-- font size in points -->
+ <weight>bold</weight>
+ <!-- 'bold' or 'normal' -->
+ <slant>normal</slant>
+ <!-- 'italic' or 'normal' -->
+ </font>
+ <font place="MenuHeader">
+ <name>sans</name>
+ <size>9</size>
+ <!-- font size in points -->
+ <weight>normal</weight>
+ <!-- 'bold' or 'normal' -->
+ <slant>normal</slant>
+ <!-- 'italic' or 'normal' -->
+ </font>
+ <font place="MenuItem">
+ <name>sans</name>
+ <size>9</size>
+ <!-- font size in points -->
+ <weight>normal</weight>
+ <!-- 'bold' or 'normal' -->
+ <slant>normal</slant>
+ <!-- 'italic' or 'normal' -->
+ </font>
+ <font place="OnScreenDisplay">
+ <name>sans</name>
+ <size>9</size>
+ <!-- font size in points -->
+ <weight>bold</weight>
+ <!-- 'bold' or 'normal' -->
+ <slant>normal</slant>
+ <!-- 'italic' or 'normal' -->
+ </font>
+</theme>
+
+<desktops>
+ <!-- this stuff is only used at startup, pagers allow you to change them
+ during a session
+
+ these are default values to use when other ones are not already set
+ by other applications, or saved in your session
+
+ use obconf if you want to change these without having to log out
+ and back in -->
+ <number>4</number>
+ <firstdesk>1</firstdesk>
+ <names>
+ <!-- set names up here if you want to, like this:
+ <name>desktop 1</name>
+ <name>desktop 2</name>
+ -->
+ </names>
+</desktops>
+
+<resize>
+ <drawContents>yes</drawContents>
+ <popupShow>Nonpixel</popupShow>
+ <!-- 'Always', 'Never', or 'Nonpixel' (xterms and such) -->
+ <popupPosition>Center</popupPosition>
+ <!-- 'Center' or 'Top' -->
+</resize>
+
+<dock>
+ <position>TopLeft</position>
+ <!-- (Top|Bottom)(Left|Right|)|Top|Bottom|Left|Right|Floating -->
+ <floatingX>0</floatingX>
+ <floatingY>0</floatingY>
+ <noStrut>no</noStrut>
+ <stacking>Above</stacking>
+ <!-- 'Above', 'Normal', or 'Below' -->
+ <direction>Vertical</direction>
+ <!-- 'Vertical' or 'Horizontal' -->
+ <autoHide>no</autoHide>
+ <hideDelay>300</hideDelay>
+ <!-- in milliseconds (1000 = 1 second) -->
+ <showDelay>300</showDelay>
+ <!-- in milliseconds (1000 = 1 second) -->
+ <moveButton>Middle</moveButton>
+ <!-- 'Left', 'Middle', 'Right' -->
+</dock>
+
+<keyboard>
+ <chainQuitKey>C-g</chainQuitKey>
+
+ <!-- Keybindings for desktop switching -->
+ <keybind key="C-A-Left">
+ <action name="DesktopLeft"><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="C-A-Right">
+ <action name="DesktopRight"><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="C-A-Up">
+ <action name="DesktopUp"><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="C-A-Down">
+ <action name="DesktopDown"><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="S-A-Left">
+ <action name="SendToDesktopLeft"><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="S-A-Right">
+ <action name="SendToDesktopRight"><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="S-A-Up">
+ <action name="SendToDesktopUp"><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="S-A-Down">
+ <action name="SendToDesktopDown"><wrap>no</wrap></action>
+ </keybind>
+ <keybind key="W-F1">
+ <action name="Desktop"><desktop>1</desktop></action>
+ </keybind>
+ <keybind key="W-F2">
+ <action name="Desktop"><desktop>2</desktop></action>
+ </keybind>
+ <keybind key="W-F3">
+ <action name="Desktop"><desktop>3</desktop></action>
+ </keybind>
+ <keybind key="W-F4">
+ <action name="Desktop"><desktop>4</desktop></action>
+ </keybind>
+ <keybind key="W-d">
+ <action name="ToggleShowDesktop"/>
+ </keybind>
+
+ <!-- Keybindings for windows -->
+ <keybind key="A-F4">
+ <action name="Close"/>
+ </keybind>
+ <keybind key="A-Escape">
+ <action name="Lower"/>
+ </keybind>
+ <keybind key="A-space">
+ <action name="ShowMenu"><menu>client-menu</menu></action>
+ </keybind>
+
+ <!-- Keybindings for window switching -->
+ <keybind key="A-Tab">
+ <action name="NextWindow"/>
+ </keybind>
+ <keybind key="A-S-Tab">
+ <action name="PreviousWindow"/>
+ </keybind>
+ <keybind key="C-A-Tab">
+ <action name="NextWindow">
+ <panels>yes</panels><desktop>yes</desktop>
+ </action>
+ </keybind>
+
+ <!-- Keybindings for running applications -->
+ <keybind key="W-e">
+ <action name="Execute">
+ <startupnotify>
+ <enabled>true</enabled>
+ <name>Konqueror</name>
+ </startupnotify>
+ <command>kfmclient openProfile filemanagement</command>
+ </action>
+ </keybind>
+</keyboard>
+
+<mouse>
+ <dragThreshold>8</dragThreshold>
+ <!-- number of pixels the mouse must move before a drag begins -->
+ <doubleClickTime>500</doubleClickTime>
+ <!-- in milliseconds (1000 = 1 second) -->
+
+ <context name="Frame">
+ <mousebind button="A-Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="A-Left" action="Click">
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="A-Left" action="Drag">
+ <action name="Move"/>
+ </mousebind>
+
+ <mousebind button="A-Right" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="A-Right" action="Press">
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="A-Right" action="Drag">
+ <action name="Resize"/>
+ </mousebind>
+
+ <mousebind button="A-Middle" action="Press">
+ <action name="Focus"/>
+ <action name="Lower"/>
+ </mousebind>
+
+ <mousebind button="A-Up" action="Click">
+ <action name="DesktopPrevious"/>
+ </mousebind>
+ <mousebind button="A-Down" action="Click">
+ <action name="DesktopNext"/>
+ </mousebind>
+ <mousebind button="C-A-Up" action="Click">
+ <action name="DesktopPrevious"/>
+ </mousebind>
+ <mousebind button="C-A-Down" action="Click">
+ <action name="DesktopNext"/>
+ </mousebind>
+ <mousebind button="A-S-Up" action="Click">
+ <action name="SendToDesktopPrevious"/>
+ </mousebind>
+ <mousebind button="A-S-Down" action="Click">
+ <action name="SendToDesktopNext"/>
+ </mousebind>
+ </context>
+
+ <context name="Titlebar">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Left" action="Drag">
+ <action name="Move"/>
+ </mousebind>
+ <mousebind button="Left" action="DoubleClick">
+ <action name="ToggleMaximizeFull"/>
+ </mousebind>
+
+ <mousebind button="Middle" action="Press">
+ <action name="Focus"/>
+ <action name="Lower"/>
+ </mousebind>
+
+ <mousebind button="Up" action="Click">
+ <action name="Shade"/>
+ </mousebind>
+ <mousebind button="Down" action="Click">
+ <action name="Unshade"/>
+ </mousebind>
+
+ <mousebind button="Right" action="Press">
+ <action name="Focus"/>
+ <action name="ShowMenu"><menu>client-menu</menu></action>
+ </mousebind>
+ </context>
+
+ <context name="Top">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Left" action="Drag">
+ <action name="Resize"><edge>top</edge></action>
+ </mousebind>
+ </context>
+
+ <context name="Left">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Left" action="Drag">
+ <action name="Resize"><edge>left</edge></action>
+ </mousebind>
+ </context>
+
+ <context name="Right">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Left" action="Drag">
+ <action name="Resize"><edge>right</edge></action>
+ </mousebind>
+ </context>
+
+ <context name="Handle">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Left" action="Drag">
+ <action name="Resize"><edge>bottom</edge></action>
+ </mousebind>
+
+ <mousebind button="Middle" action="Press">
+ <action name="Focus"/>
+ <action name="Lower"/>
+ </mousebind>
+
+ <mousebind button="Right" action="Press">
+ <action name="Focus"/>
+ <action name="ShowMenu"><menu>client-menu</menu></action>
+ </mousebind>
+ </context>
+
+ <context name="BLCorner">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Left" action="Drag">
+ <action name="Resize"/>
+ </mousebind>
+ </context>
+
+ <context name="BRCorner">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Left" action="Drag">
+ <action name="Resize"/>
+ </mousebind>
+ </context>
+
+ <context name="TLCorner">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Left" action="Drag">
+ <action name="Resize"/>
+ </mousebind>
+ </context>
+
+ <context name="TRCorner">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="Raise"/>
+ </mousebind>
+ <mousebind button="Left" action="Drag">
+ <action name="Resize"/>
+ </mousebind>
+ </context>
+
+ <context name="Client">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Middle" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Right" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ </context>
+
+ <context name="Icon">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ <action name="ShowMenu"><menu>client-menu</menu></action>
+ </mousebind>
+ <mousebind button="Right" action="Press">
+ <action name="Focus"/>
+ <action name="ShowMenu"><menu>client-menu</menu></action>
+ </mousebind>
+ </context>
+
+ <context name="AllDesktops">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="ToggleOmnipresent"/>
+ </mousebind>
+ </context>
+
+ <context name="Shade">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="ToggleShade"/>
+ </mousebind>
+ </context>
+
+ <context name="Iconify">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="Iconify"/>
+ </mousebind>
+ </context>
+
+ <context name="Maximize">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Middle" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Right" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="ToggleMaximizeFull"/>
+ </mousebind>
+ <mousebind button="Middle" action="Click">
+ <action name="ToggleMaximizeVert"/>
+ </mousebind>
+ <mousebind button="Right" action="Click">
+ <action name="ToggleMaximizeHorz"/>
+ </mousebind>
+ </context>
+
+ <context name="Close">
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Left" action="Click">
+ <action name="Close"/>
+ </mousebind>
+ </context>
+
+ <context name="Desktop">
+ <mousebind button="Up" action="Press">
+ <action name="DesktopPrevious"/>
+ </mousebind>
+ <mousebind button="Down" action="Press">
+ <action name="DesktopNext"/>
+ </mousebind>
+
+ <mousebind button="A-Up" action="Press">
+ <action name="DesktopPrevious"/>
+ </mousebind>
+ <mousebind button="A-Down" action="Press">
+ <action name="DesktopNext"/>
+ </mousebind>
+ <mousebind button="C-A-Up" action="Press">
+ <action name="DesktopPrevious"/>
+ </mousebind>
+ <mousebind button="C-A-Down" action="Press">
+ <action name="DesktopNext"/>
+ </mousebind>
+
+ <mousebind button="Left" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ <mousebind button="Right" action="Press">
+ <action name="Focus"/>
+ </mousebind>
+ </context>
+
+ <context name="Root">
+ <!-- Menus -->
+ <mousebind button="Middle" action="Press">
+ <action name="ShowMenu"><menu>client-list-combined-menu</menu></action>
+ </mousebind>
+ <mousebind button="Right" action="Press">
+ <action name="ShowMenu"><menu>root-menu</menu></action>
+ </mousebind>
+ </context>
+
+ <context name="MoveResize">
+ <mousebind button="Up" action="Press">
+ <action name="DesktopPrevious"/>
+ </mousebind>
+ <mousebind button="Down" action="Press">
+ <action name="DesktopNext"/>
+ </mousebind>
+ <mousebind button="A-Up" action="Press">
+ <action name="DesktopPrevious"/>
+ </mousebind>
+ <mousebind button="A-Down" action="Press">
+ <action name="DesktopNext"/>
+ </mousebind>
+ </context>
+</mouse>
+
+<menu>
+ <!-- You can specify more than one menu file in here and they are all loaded,
+ just don't make menu ids clash or, well, it'll be kind of pointless -->
+
+ <!-- default menu file (or custom one in $HOME/.config/openbox/) -->
+ <file>menu.xml</file>
+ <hideDelay>200</hideDelay>
+ <middle>no</middle>
+ <submenuShowDelay>100</submenuShowDelay>
+ <applicationIcons>yes</applicationIcons>
+</menu>
+
+<applications>
+<!--
+ # this is an example with comments through out. use these to make your
+ # own rules, but without the comments of course.
+
+ <application name="first element of window's WM_CLASS property (see xprop)"
+ class="second element of window's WM_CLASS property (see xprop)"
+ role="the window's WM_WINDOW_ROLE property (see xprop)">
+ # the name or the class can be set, or both. this is used to match
+ # windows when they appear. role can optionally be set as well, to
+ # further restrict your matches.
+
+ # the name, class, and role use simple wildcard matching such as those
+ # used by a shell. you can use * to match any characters and ? to match
+ # any single character.
+
+ # when multiple rules match a window, they will all be applied, in the
+ # order that they appear in this list
+
+
+ # each element can be left out or set to 'default' to specify to not
+ # change that attribute of the window
+
+ <decor>yes</decor>
+ # enable or disable window decorations
+
+ <shade>no</shade>
+ # make the window shaded when it appears, or not
+
+ <position>
+ # the position is only used if both an x and y coordinate are provided
+ # (and not set to 'default')
+ <x>center</x>
+ # a number like 50, or 'center' to center on screen
+ <y>200</y>
+ # a number like 50, or 'center' to center on screen
+ <monitor>1</monitor>
+ # specifies the monitor in a xinerama setup.
+ # 1 is the first head, or 'mouse' for wherever the mouse is
+ </position>
+
+ <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
+ # restrictions may apply, but Openbox will try to
+
+ <desktop>1</desktop>
+ # 1 is the first desktop, 'all' for all desktops
+
+ <layer>normal</layer>
+ # 'above', 'normal', or 'below'
+
+ <iconic>no</iconic>
+ # make the window iconified when it appears, or not
+
+ <skip_pager>no</skip_pager>
+ # asks to not be shown in pagers
+
+ <skip_taskbar>no</skip_taskbar>
+ # asks to not be shown in taskbars. window cycling actions will also
+ # skip past such windows
+
+ <fullscreen>yes</fullscreen>
+ # make the window in fullscreen mode when it appears
+
+ <maximized>true</maximized>
+ # 'Horizontal', 'Vertical' or boolean (yes/no)
+ </application>
+
+ # end of the example
+-->
+</applications>
+
+</openbox_config>
diff --git a/gettext.h b/gettext.h
new file mode 100644
index 0000000..d71caf0
--- /dev/null
+++ b/gettext.h
@@ -0,0 +1,91 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ gettext.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+/* Convenience header for conditional use of GNU <libintl.h>.
+ Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published
+ by the Free Software Foundation; either version 2, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA. */
+
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H 1
+
+
+/* NLS can be disabled through the configure --disable-nls option. */
+#if ENABLE_NLS
+
+/* Get declarations of GNU message catalog functions. */
+# include <libintl.h>
+
+#else
+
+/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
+ chokes if dcgettext is defined as a macro. So include it now, to make
+ later inclusions of <locale.h> a NOP. We don't include <libintl.h>
+ as well because people using "gettext.h" will not include <libintl.h>,
+ and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
+ is OK. */
+#if defined(__sun)
+# include <locale.h>
+#endif
+
+/* Disabled NLS.
+ The casts to 'const char *' serve the purpose of producing warnings
+ for invalid uses of the value returned from these functions.
+ On pre-ANSI systems without 'const', the config.h file is supposed to
+ contain "#define const". */
+# define gettext(Msgid) ((const char *) (Msgid))
+# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
+# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
+# define ngettext(Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
+# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
+
+#endif
+
+/* A pseudo function call that serves as a marker for the automated
+ extraction of messages, but does not call gettext(). The run-time
+ translation is done at a different place in the code.
+ The argument, String, should be a literal string. Concatenated strings
+ and other string expressions won't work.
+ The macro's expansion is not parenthesized, so that it is suitable as
+ initializer for static 'char[]' or 'const char[]' variables. */
+#define gettext_noop(String) String
+
+/* Custom macro to make life easier */
+#define _(str) gettext(str)
+
+#endif /* _LIBGETTEXT_H */
diff --git a/install-sh b/install-sh
new file mode 100755
index 0000000..36f96f3
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,276 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd=$cpprog
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd=$stripprog
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "$0: no input file specified" >&2
+ exit 1
+else
+ :
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d "$dst" ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=$mkdirprog
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f "$src" ] || [ -d "$src" ]
+ then
+ :
+ else
+ echo "$0: $src does not exist" >&2
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "$0: no destination specified" >&2
+ exit 1
+ else
+ :
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d "$dst" ]
+ then
+ dst=$dst/`basename "$src"`
+ else
+ :
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+ '
+IFS="${IFS-$defaultIFS}"
+
+oIFS=$IFS
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS=$oIFS
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp=$pathcomp$1
+ shift
+
+ if [ ! -d "$pathcomp" ] ;
+ then
+ $mkdirprog "$pathcomp"
+ else
+ :
+ fi
+
+ pathcomp=$pathcomp/
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd "$dst" &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dst"; else : ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dst"; else : ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dst"; else : ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dst"; else : ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename "$dst"`
+ else
+ dstfile=`basename "$dst" $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename "$dst"`
+ else
+ :
+ fi
+
+# Make a couple of temp file names in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+ rmtmp=$dstdir/#rm.$$#
+
+# Trap to clean up temp files at exit.
+
+ trap 'status=$?; rm -f "$dsttmp" "$rmtmp" && exit $status' 0
+ trap '(exit $?); exit' 1 2 13 15
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd "$src" "$dsttmp" &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dsttmp"; else :;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dsttmp"; else :;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dsttmp"; else :;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dsttmp"; else :;fi &&
+
+# Now remove or move aside any old file at destination location. We try this
+# two ways since rm can't unlink itself on some systems and the destination
+# file might be busy for other reasons. In this case, the final cleanup
+# might fail but the new file should still install successfully.
+
+{
+ if [ -f "$dstdir/$dstfile" ]
+ then
+ $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null ||
+ $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null ||
+ {
+ echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
+ (exit 1); exit
+ }
+ else
+ :
+ fi
+} &&
+
+# Now rename the file to the real destination.
+
+ $doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
+
+fi &&
+
+# The final little trick to "correctly" pass the exit status to the exit trap.
+
+{
+ (exit 0); exit
+}
diff --git a/m4/Makefile.am b/m4/Makefile.am
new file mode 100644
index 0000000..da7255e
--- /dev/null
+++ b/m4/Makefile.am
@@ -0,0 +1,33 @@
+EXTRA_DIST=\
+ codeset.m4 \
+ gettext.m4 \
+ glibc21.m4 \
+ glibc2.m4 \
+ iconv.m4 \
+ intdiv0.m4 \
+ intmax.m4 \
+ inttypes_h.m4 \
+ inttypes-h.m4 \
+ inttypes-pri.m4 \
+ lcmessage.m4 \
+ lib-ld.m4 \
+ lib-link.m4 \
+ lib-prefix.m4 \
+ lock.m4 \
+ longdouble.m4 \
+ longlong.m4 \
+ nls.m4 \
+ openbox.m4 \
+ po.m4 \
+ printf-posix.m4 \
+ progtest.m4 \
+ signed.m4 \
+ size_max.m4 \
+ stdint_h.m4 \
+ uintmax_t.m4 \
+ ulonglong.m4 \
+ visibility.m4 \
+ wchar_t.m4 \
+ wint_t.m4 \
+ x11.m4 \
+ xsize.m4
diff --git a/m4/openbox.m4 b/m4/openbox.m4
new file mode 100644
index 0000000..0c37a58
--- /dev/null
+++ b/m4/openbox.m4
@@ -0,0 +1,122 @@
+# OB_DEBUG()
+#
+# Check if the user has requested a debug build.
+# Sets the DEBUG or NDEBUG variables as appropriate
+# Sets the CVS environment variable when building CVS sources.
+AC_DEFUN([OB_DEBUG],
+[
+ AC_MSG_CHECKING([build type])
+
+ AC_ARG_ENABLE([strict-ansi],
+ AC_HELP_STRING([--enable-strict-ansi],[Enable strict ANSI compliance build [[default=no]]]),
+ [STRICT=$enableval], [STRICT="no"])
+
+ AC_ARG_ENABLE([super-warnings],
+ AC_HELP_STRING([--enable-super-warnings],[Enable extra compiler warnings [[default=no]]]),
+ [SUPERWARN=$enableval], [SUPERWARN="no"])
+
+ AC_ARG_ENABLE([debug],
+ AC_HELP_STRING([--enable-debug],[build a debug version [[default=no]]]),
+ [DEBUG=$enableval], [DEBUG="no"])
+
+ AC_ARG_ENABLE([gprof],
+ AC_HELP_STRING([--enable-gprof],[Enable gprof profiling output [[default=no]]]),
+ [PROF=$enableval], [PROF="no"])
+
+ AC_ARG_ENABLE([gprof-libc],
+ AC_HELP_STRING([--enable-gprof-libc],[Link against libc with profiling support [[default=no]]]),
+ [PROFLC=$enableval], [PROFLC="no"])
+
+ if test "$PROFLC" = "yes"; then
+ PROF="yes" # always enable profiling then
+ fi
+
+ TEST=""
+ test "${PACKAGE_VERSION%*alpha*}" != "$PACKAGE_VERSION" && TEST="yes"
+ test "${PACKAGE_VERSION%*beta*}" != "$PACKAGE_VERSION" && TEST="yes"
+ test "$TEST" = "yes" && DEBUG="yes"
+
+ if test "$DEBUG" = "yes"; then
+ MSG="DEBUG"
+ else
+ MSG="RELEASE"
+ fi
+ if test "$TEST" = "yes"; then
+ MSG="$MSG (test release)"
+ fi
+ if test "$STRICT" = "yes"; then
+ MSG="$MSG with strict ANSI compliance"
+ fi
+ if test "$SUPERWARN" = "yes"; then
+ MSG="$MSG with super warnings"
+ fi
+ AC_MSG_RESULT([$MSG])
+
+ test "$DEBUG" = "yes" && \
+ AC_DEFINE([DEBUG], [1], [Creating a debug build])
+])
+
+
+# OB_COMPILER_FLAGS()
+#
+# Check what compiler is being used for compilation.
+# It sets the CFLAGS variable appropriately for the compiler, including flags
+# for debug builds.
+AC_DEFUN([OB_COMPILER_FLAGS],
+[
+ AC_REQUIRE([AC_PROG_CPP])
+ AC_REQUIRE([AC_PROG_CC])
+
+ FLAGS=""
+ L=""
+
+ if test "$DEBUG" = "yes"; then
+ FLAGS="-DDEBUG"
+ else
+ FLAGS="-DNDEBUG -DG_DISABLE_ASSERT"
+ fi
+
+ # Check what compiler we are using
+ AC_MSG_CHECKING([for GNU CC])
+ if test "$GCC" = "yes"; then
+ AC_MSG_RESULT([yes])
+ if test "$DEBUG" = "yes"; then
+ FLAGS="$FLAGS -O0 -ggdb -fno-inline -Wwrite-strings"
+ FLAGS="$FLAGS -Wall -Wsign-compare -Waggregate-return"
+ FLAGS="$FLAGS -Wbad-function-cast -Wpointer-arith"
+ FLAGS="$FLAGS -Wno-write-strings"
+ # for Python.h
+ #FLAGS="$FLAGS -Wno-long-long"
+ fi
+ if test "$SUPERWARN" = "yes"; then
+ # glib can't handle -Wcast-qual
+ FLAGS="$FLAGS -Wcast-qual -Wextra"
+ fi
+ if test "$STRICT" = "yes"; then
+ FLAGS="$FLAGS -ansi -pedantic -D_XOPEN_SOURCE"
+ fi
+ if test "$PROF" = "yes"; then
+ FLAGS="$FLAGS -pg -fno-inline"
+ fi
+ if test "$PROFLC" = "yes"; then
+ L="$L -lc_p -lm_p"
+ fi
+ FLAGS="$FLAGS -fno-strict-aliasing"
+ fi
+ AC_MSG_CHECKING([for compiler specific flags])
+ AC_MSG_RESULT([$FLAGS])
+ CFLAGS="$CFLAGS $FLAGS"
+ LIBS="$LIBS $L"
+])
+
+AC_DEFUN([OB_NLS],
+[
+ AC_ARG_ENABLE([nls],
+ AC_HELP_STRING([--enable-nls],[Enable NLS translations [[default=yes]]]),
+ [NLS=$enableval], [NLS="yes"])
+
+ if test "$NLS" = yes; then
+ DEFS="$DEFS -DENABLE_NLS"
+ fi
+])
+
diff --git a/m4/x11.m4 b/m4/x11.m4
new file mode 100644
index 0000000..f3b1ede
--- /dev/null
+++ b/m4/x11.m4
@@ -0,0 +1,443 @@
+# X11_DEVEL()
+#
+# Check for the presence of the X Window System headers and libraries.
+# Sets the CPPFLAGS and LIBS variables as appropriate.
+AC_DEFUN([X11_DEVEL],
+[
+ AC_PATH_XTRA
+ test "$no_x" = "yes" && \
+ AC_MSG_ERROR([The X Window System could not be found.])
+
+ # Store these
+ OLDLIBS=$LIBS
+ OLDCPPFLAGS=$CPPFLAGS
+
+ CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+ X_LIBS="$X_PRE_LIBS $X_LIBS -lX11"
+ LIBS="$LIBS $X_LIBS"
+
+ # Check for required functions in -lX11
+ AC_CHECK_LIB(
+ [X11], [XOpenDisplay],
+ ,
+ AC_MSG_ERROR([Could not find XOpenDisplay in -lX11.])
+ )
+
+ # Restore the old values. Use X_CFLAGS and X_LIBS in
+ # the Makefiles
+ LIBS=$OLDLIBS
+ CPPFLAGS=$OLDCPPFLAGS
+])
+
+# X11_EXT_XKB()
+#
+# Check for the presence of the "Xkb" X Window System extension.
+# Defines "XKB" and sets the $(XKB) variable to "yes" if the extension is
+# present.
+AC_DEFUN([X11_EXT_XKB],
+[
+ AC_REQUIRE([X11_DEVEL])
+
+ AC_ARG_ENABLE([xkb],
+ AC_HELP_STRING(
+ [--disable-xkb],
+ [build without support for xkb extension [default=enabled]]),
+ [USE=$enableval], [USE="yes"])
+
+ if test "$USE" = "yes"; then
+ # Store these
+ OLDLIBS=$LIBS
+ OLDCPPFLAGS=$CPPFLAGS
+
+ CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+ LIBS="$LIBS $X_LIBS"
+
+ AC_CHECK_LIB([X11], [XkbBell],
+ AC_MSG_CHECKING([for X11/XKBlib.h])
+ AC_TRY_LINK(
+ [
+ #include <X11/Xlib.h>
+ #include <X11/Xutil.h>
+ #include <X11/XKBlib.h>
+ ],
+ [
+ Display *d;
+ Window w;
+ XkbBell(d, w, 0, 0);
+ ],
+ [
+ AC_MSG_RESULT([yes])
+ XKB="yes"
+ AC_DEFINE([XKB], [1], [Found the XKB extension])
+
+ XKB_CFLAGS=""
+ XKB_LIBS=""
+ AC_SUBST(XKB_CFLAGS)
+ AC_SUBST(XKB_LIBS)
+ ],
+ [
+ AC_MSG_RESULT([no])
+ XKB="no"
+ ])
+ )
+
+ LIBS=$OLDLIBS
+ CPPFLAGS=$OLDCPPFLAGS
+ fi
+
+ AC_MSG_CHECKING([for the Xkb extension])
+ if test "$XKB" = "yes"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
+# X11_EXT_XRANDR()
+#
+# Check for the presence of the "XRandR" X Window System extension.
+# Defines "XRANDR" and sets the $(XRANDR) variable to "yes" if the extension is
+# present.
+AC_DEFUN([X11_EXT_XRANDR],
+[
+ AC_REQUIRE([X11_DEVEL])
+
+ AC_ARG_ENABLE([xrandr],
+ AC_HELP_STRING(
+ [--disable-xrandr],
+ [build without support for xrandr extension [default=enabled]]),
+ [USE=$enableval], [USE="yes"])
+
+ if test "$USE" = "yes"; then
+ # Store these
+ OLDLIBS=$LIBS
+ OLDCPPFLAGS=$CPPFLAGS
+
+ CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+ LIBS="$LIBS $X_LIBS -lXext -lXrender -lXrandr"
+
+ AC_CHECK_LIB([Xrandr], [XRRSelectInput],
+ AC_MSG_CHECKING([for X11/extensions/Xrandr.h])
+ AC_TRY_LINK(
+ [
+ #include <X11/Xlib.h>
+ #include <X11/extensions/Xrandr.h>
+ ],
+ [
+ Display *d;
+ Drawable r;
+ int i;
+ XRRQueryExtension(d, &i, &i);
+ XRRGetScreenInfo(d, r);
+ ],
+ [
+ AC_MSG_RESULT([yes])
+ XRANDR="yes"
+ AC_DEFINE([XRANDR], [1], [Found the XRandR extension])
+
+ XRANDR_CFLAGS=""
+ XRANDR_LIBS="-lXext -lXrender -lXrandr"
+ AC_SUBST(XRANDR_CFLAGS)
+ AC_SUBST(XRANDR_LIBS)
+ ],
+ [
+ AC_MSG_RESULT([no])
+ XRANDR="no"
+ ])
+ )
+
+ LIBS=$OLDLIBS
+ CPPFLAGS=$OLDCPPFLAGS
+ fi
+
+ AC_MSG_CHECKING([for the XRandR extension])
+ if test "$XRANDR" = "yes"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
+# X11_EXT_SHAPE()
+#
+# Check for the presence of the "Shape" X Window System extension.
+# Defines "SHAPE", sets the $(SHAPE) variable to "yes", and sets the $(LIBS)
+# appropriately if the extension is present.
+AC_DEFUN([X11_EXT_SHAPE],
+[
+ AC_REQUIRE([X11_DEVEL])
+
+ AC_ARG_ENABLE([xshape],
+ AC_HELP_STRING(
+ [--disable-xshape],
+ [build without support for xshape extension [default=enabled]]),
+ [USE=$enableval], [USE="yes"])
+
+ if test "$USE" = "yes"; then
+ # Store these
+ OLDLIBS=$LIBS
+ OLDCPPFLAGS=$CPPFLAGS
+
+ CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+ LIBS="$LIBS $X_LIBS"
+
+ AC_CHECK_LIB([Xext], [XShapeCombineShape],
+ AC_MSG_CHECKING([for X11/extensions/shape.h])
+ AC_TRY_LINK(
+ [
+ #include <X11/Xlib.h>
+ #include <X11/Xutil.h>
+ #include <X11/extensions/shape.h>
+ ],
+ [
+ long foo = ShapeSet;
+ ],
+ [
+ AC_MSG_RESULT([yes])
+ SHAPE="yes"
+ AC_DEFINE([SHAPE], [1], [Found the XShape extension])
+
+ XSHAPE_CFLAGS=""
+ XSHAPE_LIBS="-lXext"
+ AC_SUBST(XSHAPE_CFLAGS)
+ AC_SUBST(XSHAPE_LIBS)
+ ],
+ [
+ AC_MSG_RESULT([no])
+ SHAPE="no"
+ ])
+ )
+
+ LIBS=$OLDLIBS
+ CPPFLAGS=$OLDCPPFLAGS
+ fi
+
+ AC_MSG_CHECKING([for the Shape extension])
+ if test "$SHAPE" = "yes"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
+
+# X11_EXT_XINERAMA()
+#
+# Check for the presence of the "Xinerama" X Window System extension.
+# Defines "XINERAMA", sets the $(XINERAMA) variable to "yes", and sets the
+# $(LIBS) appropriately if the extension is present.
+AC_DEFUN([X11_EXT_XINERAMA],
+[
+ AC_REQUIRE([X11_DEVEL])
+
+ AC_ARG_ENABLE([xinerama],
+ AC_HELP_STRING(
+ [--disable-xinerama],
+ [build without support for xinerama [default=enabled]]),
+ [USE=$enableval], [USE="yes"])
+
+ if test "$USE" = "yes"; then
+ # Store these
+ OLDLIBS=$LIBS
+ OLDCPPFLAGS=$CPPFLAGS
+
+ CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+ LIBS="$LIBS $X_LIBS -lXext"
+
+ AC_CHECK_LIB([Xinerama], [XineramaQueryExtension],
+ [
+ AC_MSG_CHECKING([for X11/extensions/Xinerama.h])
+ AC_TRY_LINK(
+ [
+ #include <X11/Xlib.h>
+ #include <X11/extensions/Xinerama.h>
+ ],
+ [
+ XineramaScreenInfo foo;
+ ],
+ [
+ AC_MSG_RESULT([yes])
+ XINERAMA="yes"
+ AC_DEFINE([XINERAMA], [1], [Enable support of the Xinerama extension])
+ XINERAMA_LIBS="-lXext -lXinerama"
+ AC_SUBST(XINERAMA_LIBS)
+ ],
+ [
+ AC_MSG_RESULT([no])
+ XINERAMA="no"
+ ])
+ ])
+
+ LIBS=$OLDLIBS
+ CPPFLAGS=$OLDCPPFLAGS
+ fi
+
+ AC_MSG_CHECKING([for the Xinerama extension])
+ if test "$XINERAMA" = "yes"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
+# X11_EXT_SYNC()
+#
+# Check for the presence of the "Sync" X Window System extension.
+# Defines "SYNC", sets the $(SYNC) variable to "yes", and sets the $(LIBS)
+# appropriately if the extension is present.
+AC_DEFUN([X11_EXT_SYNC],
+[
+ AC_REQUIRE([X11_DEVEL])
+
+ AC_ARG_ENABLE([xsync],
+ AC_HELP_STRING(
+ [--disable-xsync],
+ [build without support for xsync extension [default=enabled]]),
+ [USE=$enableval], [USE="yes"])
+
+ if test "$USE" = "yes"; then
+ # Store these
+ OLDLIBS=$LIBS
+ OLDCPPFLAGS=$CPPFLAGS
+
+ CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+ LIBS="$LIBS $X_LIBS"
+
+ AC_CHECK_LIB([Xext], [XSyncInitialize],
+ AC_MSG_CHECKING([for X11/extensions/sync.h])
+ AC_TRY_LINK(
+ [
+ #include <X11/Xlib.h>
+ #include <X11/Xutil.h>
+ #include <X11/extensions/sync.h>
+ ],
+ [
+ XSyncValueType foo;
+ ],
+ [
+ AC_MSG_RESULT([yes])
+ SYNC="yes"
+ AC_DEFINE([SYNC], [1], [Found the XSync extension])
+
+ XSYNC_CFLAGS=""
+ XSYNC_LIBS="-lXext"
+ AC_SUBST(XSYNC_CFLAGS)
+ AC_SUBST(XSYNC_LIBS)
+ ],
+ [
+ AC_MSG_RESULT([no])
+ SYNC="no"
+ ])
+ )
+
+ LIBS=$OLDLIBS
+ CPPFLAGS=$OLDCPPFLAGS
+ fi
+
+ AC_MSG_CHECKING([for the Sync extension])
+ if test "$SYNC" = "yes"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
+# X11_EXT_AUTH()
+#
+# Check for the presence of the "Xau" X Window System extension.
+# Defines "AUTH, sets the $(AUTH) variable to "yes", and sets the $(LIBS)
+# appropriately if the extension is present.
+AC_DEFUN([X11_EXT_AUTH],
+[
+ AC_REQUIRE([X11_DEVEL])
+
+ # Store these
+ OLDLIBS=$LIBS
+ OLDCPPFLAGS=$CPPFLAGS
+
+ CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+ LIBS="$LIBS $X_LIBS"
+
+ AC_CHECK_LIB([Xau], [XauReadAuth],
+ AC_MSG_CHECKING([for X11/Xauth.h])
+ AC_TRY_LINK(
+ [
+ #include <X11/Xlib.h>
+ #include <X11/Xutil.h>
+ #include <X11/Xauth.h>
+ ],
+ [
+ ],
+ [
+ AC_MSG_RESULT([yes])
+ AUTH="yes"
+ AC_DEFINE([AUTH], [1], [Found the Xauth extension])
+
+ XAUTH_CFLAGS=""
+ XAUTH_LIBS="-lXau"
+ AC_SUBST(XAUTH_CFLAGS)
+ AC_SUBST(XAUTH_LIBS)
+ ],
+ [
+ AC_MSG_RESULT([no])
+ AUTH="no"
+ ])
+ )
+
+ LIBS=$OLDLIBS
+ CPPFLAGS=$OLDCPPFLAGS
+
+ AC_MSG_CHECKING([for the Xauth extension])
+ if test "$AUTH" = "yes"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
+
+# X11_SM()
+#
+# Check for the presence of SMlib for session management.
+# Defines "USE_SM" if SMlib is present.
+AC_DEFUN([X11_SM],
+[
+ AC_REQUIRE([X11_DEVEL])
+
+ AC_ARG_ENABLE([session-management],
+ AC_HELP_STRING(
+ [--disable-session-management],
+ [build without support for session managers [default=enabled]]),
+ [SM=$enableval], [SM="yes"])
+
+ if test "$SM" = "yes"; then
+ # Store these
+ OLDLIBS=$LIBS
+ OLDCPPFLAGS=$CPPFLAGS
+
+ CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+ LIBS="$LIBS $X_LIBS"
+
+ SM="no"
+
+ AC_CHECK_LIB([SM], [SmcSaveYourselfDone], [
+ AC_CHECK_HEADERS([X11/SM/SMlib.h], [
+ SM_CFLAGS="$X_CFLAGS"
+ SM_LIBS="-lSM -lICE"
+ AC_DEFINE(USE_SM, 1, [Use session management])
+ AC_SUBST(SM_CFLAGS)
+ AC_SUBST(SM_LIBS)
+ SM="yes"
+ ])
+ ])
+
+ LIBS=$OLDLIBS
+ CPPFLAGS=$OLDCPPFLAGS
+ fi
+
+ AC_MSG_CHECKING([for session management support])
+ if test "$SM" = "yes"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+])
diff --git a/makedeb b/makedeb
new file mode 100755
index 0000000..ec0d620
--- /dev/null
+++ b/makedeb
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+if test "$1" = "ubuntu"; then
+ APPEND="ubuntu1";
+else
+ if test "$1" = "debian"; then
+ APPEND="";
+ else
+ echo "Usage: makedeb [ubuntu|debian]";
+ exit 1;
+ fi
+fi
+
+# get the version from configure.ac
+VERSION=$(grep AC_INIT\(\\\[openbox\\] configure.ac|cut -d [ -f 3-|cut -d ] -f 1)
+
+echo
+echo "Building deb for openbox-$VERSION-0$APPEND"
+echo
+
+TIME="$(date '+%a, %d %B %Y %T %z')"
+
+IN=debian/changelog.in
+OUT=debian/changelog
+
+# make sure configure gets run with the right parameters
+make distclean > /dev/null || rm -f config.status
+
+rm -f $OUT
+
+sed -e "s!@time@!$TIME!" -e "s!@version@!$VERSION-0$APPEND!" $IN >$OUT && \
+fakeroot debian/rules binary && \
+make distclean > /dev/null
diff --git a/obrender/Makefile b/obrender/Makefile
new file mode 100644
index 0000000..b90edac
--- /dev/null
+++ b/obrender/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/obrender/button.c b/obrender/button.c
new file mode 100644
index 0000000..14a454d
--- /dev/null
+++ b/obrender/button.c
@@ -0,0 +1,117 @@
+#include "render.h"
+#include "button.h"
+#include "instance.h"
+#include "mask.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <string.h>
+
+RrButton *RrButtonNew (const RrInstance *inst)
+{
+ RrButton *out = NULL;
+
+ out = g_new(RrButton, 1);
+ out->inst = inst;
+
+ /* no need to alloc colors, set them null (for freeing later) */
+ out->focused_unpressed_color = NULL;
+ out->unfocused_unpressed_color = NULL;
+ out->focused_pressed_color = NULL;
+ out->unfocused_pressed_color = NULL;
+ out->disabled_focused_color = NULL;
+ out->disabled_unfocused_color = NULL;
+ out->hover_focused_color = NULL;
+ out->hover_unfocused_color = NULL;
+ out->toggled_hover_focused_color = NULL;
+ out->toggled_hover_unfocused_color = NULL;
+ out->toggled_focused_pressed_color = NULL;
+ out->toggled_unfocused_pressed_color = NULL;
+ out->toggled_focused_unpressed_color = NULL;
+ out->toggled_unfocused_unpressed_color = NULL;
+
+ /* same with masks */
+ out->mask = NULL;
+ out->pressed_mask = NULL;
+ out->disabled_mask = NULL;
+ out->hover_mask = NULL;
+ out->toggled_mask = NULL;
+ out->toggled_hover_mask = NULL;
+ out->toggled_pressed_mask = NULL;
+
+ /* allocate appearances */
+ out->a_focused_unpressed = RrAppearanceNew(inst, 1);
+ out->a_unfocused_unpressed = RrAppearanceNew(inst, 1);
+ out->a_focused_pressed = RrAppearanceNew(inst, 1);
+ out->a_unfocused_pressed = RrAppearanceNew(inst, 1);
+ out->a_disabled_focused = RrAppearanceNew(inst, 1);
+ out->a_disabled_unfocused = RrAppearanceNew(inst, 1);
+ out->a_hover_focused = RrAppearanceNew(inst, 1);
+ out->a_hover_unfocused = RrAppearanceNew(inst, 1);
+ out->a_toggled_focused_unpressed = RrAppearanceNew(inst, 1);
+ out->a_toggled_unfocused_unpressed = RrAppearanceNew(inst, 1);
+ out->a_toggled_focused_pressed = RrAppearanceNew(inst, 1);
+ out->a_toggled_unfocused_pressed = RrAppearanceNew(inst, 1);
+ out->a_toggled_hover_focused = RrAppearanceNew(inst, 1);
+ out->a_toggled_hover_unfocused = RrAppearanceNew(inst, 1);
+
+ return out;
+}
+
+void RrButtonFree(RrButton *b)
+{
+ /* colors */
+ if (b->focused_unpressed_color)
+ RrColorFree(b->focused_unpressed_color);
+ if (b->unfocused_unpressed_color)
+ RrColorFree(b->unfocused_unpressed_color);
+ if (b->focused_pressed_color)
+ RrColorFree(b->focused_pressed_color);
+ if (b->unfocused_pressed_color)
+ RrColorFree(b->unfocused_pressed_color);
+ if (b->disabled_focused_color)
+ RrColorFree(b->disabled_focused_color);
+ if (b->disabled_unfocused_color)
+ RrColorFree(b->disabled_unfocused_color);
+ if (b->hover_focused_color)
+ RrColorFree(b->hover_focused_color);
+ if (b->hover_unfocused_color)
+ RrColorFree(b->hover_unfocused_color);
+ if (b->toggled_hover_focused_color)
+ RrColorFree(b->toggled_hover_focused_color);
+ if (b->toggled_hover_unfocused_color)
+ RrColorFree(b->toggled_hover_unfocused_color);
+ if (b->toggled_focused_pressed_color)
+ RrColorFree(b->toggled_focused_pressed_color);
+ if (b->toggled_unfocused_pressed_color)
+ RrColorFree(b->toggled_unfocused_pressed_color);
+ if (b->toggled_focused_unpressed_color)
+ RrColorFree(b->toggled_focused_unpressed_color);
+ if (b->toggled_unfocused_unpressed_color)
+ RrColorFree(b->toggled_unfocused_unpressed_color);
+
+ /* masks */
+ if (b->mask) RrPixmapMaskFree(b->mask);
+ if (b->pressed_mask) RrPixmapMaskFree(b->pressed_mask);
+ if (b->disabled_mask) RrPixmapMaskFree(b->disabled_mask);
+ if (b->hover_mask) RrPixmapMaskFree(b->hover_mask);
+ if (b->toggled_mask) RrPixmapMaskFree(b->toggled_mask);
+ if (b->toggled_hover_mask) RrPixmapMaskFree(b->toggled_hover_mask);
+ if (b->toggled_pressed_mask) RrPixmapMaskFree(b->toggled_pressed_mask);
+
+ /* appearances */
+ RrAppearanceFree(b->a_focused_unpressed);
+ RrAppearanceFree(b->a_unfocused_unpressed);
+ RrAppearanceFree(b->a_focused_pressed);
+ RrAppearanceFree(b->a_unfocused_pressed);
+ RrAppearanceFree(b->a_disabled_focused);
+ RrAppearanceFree(b->a_disabled_unfocused);
+ RrAppearanceFree(b->a_hover_focused);
+ RrAppearanceFree(b->a_hover_unfocused);
+ RrAppearanceFree(b->a_toggled_focused_unpressed);
+ RrAppearanceFree(b->a_toggled_unfocused_unpressed);
+ RrAppearanceFree(b->a_toggled_focused_pressed);
+ RrAppearanceFree(b->a_toggled_unfocused_pressed);
+ RrAppearanceFree(b->a_toggled_hover_focused);
+ RrAppearanceFree(b->a_toggled_hover_unfocused);
+}
diff --git a/obrender/button.h b/obrender/button.h
new file mode 100644
index 0000000..659c3ea
--- /dev/null
+++ b/obrender/button.h
@@ -0,0 +1,11 @@
+#ifndef __button_h
+#define __button_h
+
+#include "render.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <glib.h>
+
+
+#endif /* __button_h */
diff --git a/obrender/color.c b/obrender/color.c
new file mode 100644
index 0000000..221ebc4
--- /dev/null
+++ b/obrender/color.c
@@ -0,0 +1,366 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ color.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ 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 "render.h"
+#include "color.h"
+#include "instance.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <string.h>
+
+void RrColorAllocateGC(RrColor *in)
+{
+ XGCValues gcv;
+
+ gcv.foreground = in->pixel;
+ gcv.cap_style = CapProjecting;
+ in->gc = XCreateGC(RrDisplay(in->inst),
+ RrRootWindow(in->inst),
+ GCForeground | GCCapStyle, &gcv);
+}
+
+RrColor *RrColorParse(const RrInstance *inst, gchar *colorname)
+{
+ XColor xcol;
+
+ g_assert(colorname != NULL);
+ /* get rgb values from colorname */
+
+ xcol.red = 0;
+ xcol.green = 0;
+ xcol.blue = 0;
+ xcol.pixel = 0;
+ if (!XParseColor(RrDisplay(inst), RrColormap(inst), colorname, &xcol)) {
+ g_message("Unable to parse color '%s'", colorname);
+ return NULL;
+ }
+ return RrColorNew(inst, xcol.red >> 8, xcol.green >> 8, xcol.blue >> 8);
+}
+
+/*#define NO_COLOR_CACHE*/
+#ifdef DEBUG
+gint id;
+#endif
+
+RrColor *RrColorNew(const RrInstance *inst, gint r, gint g, gint b)
+{
+ /* this should be replaced with something far cooler */
+ RrColor *out = NULL;
+ XColor xcol;
+ gint key;
+
+ g_assert(r >= 0 && r < 256);
+ g_assert(g >= 0 && g < 256);
+ g_assert(b >= 0 && b < 256);
+
+ key = (r << 24) + (g << 16) + (b << 8);
+#ifndef NO_COLOR_CACHE
+ if ((out = g_hash_table_lookup(RrColorHash(inst), &key))) {
+ out->refcount++;
+ } else {
+#endif
+ xcol.red = (r << 8) | r;
+ xcol.green = (g << 8) | g;
+ xcol.blue = (b << 8) | b;
+ if (XAllocColor(RrDisplay(inst), RrColormap(inst), &xcol)) {
+ out = g_slice_new(RrColor);
+ out->inst = inst;
+ out->r = xcol.red >> 8;
+ out->g = xcol.green >> 8;
+ out->b = xcol.blue >> 8;
+ out->gc = None;
+ out->pixel = xcol.pixel;
+ out->key = key;
+ out->refcount = 1;
+#ifdef DEBUG
+ out->id = id++;
+#endif
+#ifndef NO_COLOR_CACHE
+ g_hash_table_insert(RrColorHash(inst), &out->key, out);
+ }
+#endif
+ }
+ return out;
+}
+
+RrColor *RrColorCopy(RrColor* c)
+{
+ return RrColorNew(c->inst, c->r, c->g, c->b);
+}
+
+void RrColorFree(RrColor *c)
+{
+ if (c) {
+ if (--c->refcount < 1) {
+#ifndef NO_COLOR_CACHE
+ g_assert(g_hash_table_lookup(RrColorHash(c->inst), &c->key));
+ g_hash_table_remove(RrColorHash(c->inst), &c->key);
+#endif
+ if (c->pixel) XFreeColors(RrDisplay(c->inst), RrColormap(c->inst),
+ &c->pixel, 1, 0);
+ if (c->gc) XFreeGC(RrDisplay(c->inst), c->gc);
+ g_slice_free(RrColor, c);
+ }
+ }
+}
+
+void RrReduceDepth(const RrInstance *inst, RrPixel32 *data, XImage *im)
+{
+ gint r, g, b;
+ gint x,y;
+ RrPixel32 *p32 = (RrPixel32 *) im->data;
+ RrPixel16 *p16 = (RrPixel16 *) im->data;
+ RrPixel8 *p8 = (RrPixel8 *) im->data;
+ switch (im->bits_per_pixel) {
+ case 32:
+ if ((RrRedOffset(inst) != RrDefaultRedOffset) ||
+ (RrBlueOffset(inst) != RrDefaultBlueOffset) ||
+ (RrGreenOffset(inst) != RrDefaultGreenOffset)) {
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ r = (data[x] >> RrDefaultRedOffset) & 0xFF;
+ g = (data[x] >> RrDefaultGreenOffset) & 0xFF;
+ b = (data[x] >> RrDefaultBlueOffset) & 0xFF;
+ p32[x] = (r << RrRedOffset(inst))
+ + (g << RrGreenOffset(inst))
+ + (b << RrBlueOffset(inst));
+ }
+ data += im->width;
+ p32 += im->width;
+ }
+ } else im->data = (gchar*) data;
+ break;
+ case 24:
+ {
+ /* reverse the ordering, shifting left 16bit should be the first byte
+ out of three, etc */
+ const guint roff = (16 - RrRedOffset(inst)) / 8;
+ const guint goff = (16 - RrGreenOffset(inst)) / 8;
+ const guint boff = (16 - RrBlueOffset(inst)) / 8;
+ gint outx;
+ for (y = 0; y < im->height; y++) {
+ for (x = 0, outx = 0; x < im->width; x++, outx += 3) {
+ r = (data[x] >> RrDefaultRedOffset) & 0xFF;
+ g = (data[x] >> RrDefaultGreenOffset) & 0xFF;
+ b = (data[x] >> RrDefaultBlueOffset) & 0xFF;
+ p8[outx+roff] = r;
+ p8[outx+goff] = g;
+ p8[outx+boff] = b;
+ }
+ data += im->width;
+ p8 += im->bytes_per_line;
+ }
+ break;
+ }
+ case 16:
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ r = (data[x] >> RrDefaultRedOffset) & 0xFF;
+ r = r >> RrRedShift(inst);
+ g = (data[x] >> RrDefaultGreenOffset) & 0xFF;
+ g = g >> RrGreenShift(inst);
+ b = (data[x] >> RrDefaultBlueOffset) & 0xFF;
+ b = b >> RrBlueShift(inst);
+ p16[x] = (r << RrRedOffset(inst))
+ + (g << RrGreenOffset(inst))
+ + (b << RrBlueOffset(inst));
+ }
+ data += im->width;
+ p16 += im->bytes_per_line/2;
+ }
+ break;
+ case 8:
+ if (RrVisual(inst)->class == TrueColor) {
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ r = (data[x] >> RrDefaultRedOffset) & 0xFF;
+ r = r >> RrRedShift(inst);
+ g = (data[x] >> RrDefaultGreenOffset) & 0xFF;
+ g = g >> RrGreenShift(inst);
+ b = (data[x] >> RrDefaultBlueOffset) & 0xFF;
+ b = b >> RrBlueShift(inst);
+ p8[x] = (r << RrRedOffset(inst))
+ + (g << RrGreenOffset(inst))
+ + (b << RrBlueOffset(inst));
+ }
+ data += im->width;
+ p8 += im->bytes_per_line;
+ }
+ } else {
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ p8[x] = RrPickColor(inst,
+ data[x] >> RrDefaultRedOffset,
+ data[x] >> RrDefaultGreenOffset,
+ data[x] >> RrDefaultBlueOffset)->pixel;
+ }
+ data += im->width;
+ p8 += im->bytes_per_line;
+ }
+ }
+ break;
+ default:
+ g_error("This image bit depth (%i) is currently unhandled", im->bits_per_pixel);
+
+ }
+}
+
+XColor *RrPickColor(const RrInstance *inst, gint r, gint g, gint b)
+{
+ r = (r & 0xff) >> (8-RrPseudoBPC(inst));
+ g = (g & 0xff) >> (8-RrPseudoBPC(inst));
+ b = (b & 0xff) >> (8-RrPseudoBPC(inst));
+ return &RrPseudoColors(inst)[(r << (2*RrPseudoBPC(inst))) +
+ (g << (1*RrPseudoBPC(inst))) +
+ b];
+}
+
+static void swap_byte_order(XImage *im)
+{
+ gint x, y, di;
+
+ di = 0;
+ for (y = 0; y < im->height; ++y) {
+ for (x = 0; x < im->height; ++x) {
+ gchar *c = &im->data[di + x * im->bits_per_pixel / 8];
+ gchar t;
+
+ switch (im->bits_per_pixel) {
+ case 32:
+ t = c[2];
+ c[2] = c[3];
+ c[3] = t;
+ case 16:
+ t = c[0];
+ c[0] = c[1];
+ c[1] = t;
+ case 8:
+ case 1:
+ break;
+ default:
+ g_error("Your bit depth (%i) is currently unhandled",
+ im->bits_per_pixel);
+ }
+ }
+ di += im->bytes_per_line;
+ }
+
+ if (im->byte_order == LSBFirst)
+ im->byte_order = MSBFirst;
+ else
+ im->byte_order = LSBFirst;
+}
+
+void RrIncreaseDepth(const RrInstance *inst, RrPixel32 *data, XImage *im)
+{
+ gint r, g, b;
+ gint x,y;
+ RrPixel32 *p32 = (RrPixel32 *) im->data;
+ RrPixel16 *p16 = (RrPixel16 *) im->data;
+ guchar *p8 = (guchar *)im->data;
+
+ if (im->byte_order != LSBFirst)
+ swap_byte_order(im);
+
+ switch (im->bits_per_pixel) {
+ case 32:
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ r = (p32[x] >> RrRedOffset(inst)) & 0xff;
+ g = (p32[x] >> RrGreenOffset(inst)) & 0xff;
+ b = (p32[x] >> RrBlueOffset(inst)) & 0xff;
+ data[x] = (r << RrDefaultRedOffset)
+ + (g << RrDefaultGreenOffset)
+ + (b << RrDefaultBlueOffset)
+ + (0xff << RrDefaultAlphaOffset);
+ }
+ data += im->width;
+ p32 += im->bytes_per_line/4;
+ }
+ break;
+ case 16:
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ r = (p16[x] & RrRedMask(inst)) >>
+ RrRedOffset(inst) <<
+ RrRedShift(inst);
+ g = (p16[x] & RrGreenMask(inst)) >>
+ RrGreenOffset(inst) <<
+ RrGreenShift(inst);
+ b = (p16[x] & RrBlueMask(inst)) >>
+ RrBlueOffset(inst) <<
+ RrBlueShift(inst);
+ data[x] = (r << RrDefaultRedOffset)
+ + (g << RrDefaultGreenOffset)
+ + (b << RrDefaultBlueOffset)
+ + (0xff << RrDefaultAlphaOffset);
+ }
+ data += im->width;
+ p16 += im->bytes_per_line/2;
+ }
+ break;
+ case 8:
+ g_error("This image bit depth (%i) is currently unhandled", 8);
+ break;
+ case 1:
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ if (!(((p8[x / 8]) >> (x % 8)) & 0x1))
+ data[x] = 0xff << RrDefaultAlphaOffset; /* black */
+ else
+ data[x] = 0xffffffff; /* white */
+ }
+ data += im->width;
+ p8 += im->bytes_per_line;
+ }
+ break;
+ default:
+ g_error("This image bit depth (%i) is currently unhandled",
+ im->bits_per_pixel);
+ }
+}
+
+gint RrColorRed(const RrColor *c)
+{
+ return c->r;
+}
+
+gint RrColorGreen(const RrColor *c)
+{
+ return c->g;
+}
+
+gint RrColorBlue(const RrColor *c)
+{
+ return c->b;
+}
+
+gulong RrColorPixel(const RrColor *c)
+{
+ return c->pixel;
+}
+
+GC RrColorGC(RrColor *c)
+{
+ if (!c->gc)
+ RrColorAllocateGC(c);
+ return c->gc;
+}
diff --git a/obrender/color.h b/obrender/color.h
new file mode 100644
index 0000000..26fa7af
--- /dev/null
+++ b/obrender/color.h
@@ -0,0 +1,51 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ color.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __color_h
+#define __color_h
+
+#include "render.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <glib.h>
+
+struct _RrColor {
+ const RrInstance *inst;
+
+ gint r;
+ gint g;
+ gint b;
+ gulong pixel;
+ GC gc;
+
+ gint key;
+ gint refcount;
+
+#ifdef DEBUG
+ gint id;
+#endif
+};
+
+void RrColorAllocateGC(RrColor *in);
+XColor *RrPickColor(const RrInstance *inst, gint r, gint g, gint b);
+void RrReduceDepth(const RrInstance *inst, RrPixel32 *data, XImage *im);
+void RrIncreaseDepth(const RrInstance *inst, RrPixel32 *data, XImage *im);
+
+#endif /* __color_h */
diff --git a/obrender/font.c b/obrender/font.c
new file mode 100644
index 0000000..a22e23f
--- /dev/null
+++ b/obrender/font.c
@@ -0,0 +1,382 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ font.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ 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 "font.h"
+#include "color.h"
+#include "mask.h"
+#include "theme.h"
+#include "geom.h"
+#include "instance.h"
+#include "gettext.h"
+
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+
+static void measure_font(const RrInstance *inst, RrFont *f)
+{
+ PangoFontMetrics *metrics;
+ static PangoLanguage *lang = NULL;
+
+ if (lang == NULL) {
+#if PANGO_VERSION_MAJOR > 1 || \
+ (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
+ lang = pango_language_get_default();
+#else
+ gchar *locale, *p;
+ /* get the default language from the locale
+ (based on gtk_get_default_language in gtkmain.c) */
+ locale = g_strdup(setlocale(LC_CTYPE, NULL));
+ if ((p = strchr(locale, '.'))) *p = '\0'; /* strip off the . */
+ if ((p = strchr(locale, '@'))) *p = '\0'; /* strip off the @ */
+ lang = pango_language_from_string(locale);
+ g_free(locale);
+#endif
+ }
+
+ /* measure the ascent and descent */
+ metrics = pango_context_get_metrics(inst->pango, f->font_desc, lang);
+ f->ascent = pango_font_metrics_get_ascent(metrics);
+ f->descent = pango_font_metrics_get_descent(metrics);
+ pango_font_metrics_unref(metrics);
+
+}
+
+RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size,
+ RrFontWeight weight, RrFontSlant slant)
+{
+ RrFont *out;
+ PangoWeight pweight;
+ PangoStyle pstyle;
+ PangoAttrList *attrlist;
+
+ out = g_slice_new(RrFont);
+ out->inst = inst;
+ out->ref = 1;
+ out->font_desc = pango_font_description_new();
+ out->layout = pango_layout_new(inst->pango);
+ out->shortcut_underline = pango_attr_underline_new(PANGO_UNDERLINE_LOW);
+ out->shortcut_underline->start_index = 0;
+ out->shortcut_underline->end_index = 0;
+
+ attrlist = pango_attr_list_new();
+ /* shortcut_underline is owned by the attrlist */
+ pango_attr_list_insert(attrlist, out->shortcut_underline);
+ /* the attributes are owned by the layout */
+ pango_layout_set_attributes(out->layout, attrlist);
+ pango_attr_list_unref(attrlist);
+
+ switch (weight) {
+ case RR_FONTWEIGHT_LIGHT: pweight = PANGO_WEIGHT_LIGHT; break;
+ case RR_FONTWEIGHT_NORMAL: pweight = PANGO_WEIGHT_NORMAL; break;
+ case RR_FONTWEIGHT_SEMIBOLD: pweight = PANGO_WEIGHT_SEMIBOLD; break;
+ case RR_FONTWEIGHT_BOLD: pweight = PANGO_WEIGHT_BOLD; break;
+ case RR_FONTWEIGHT_ULTRABOLD: pweight = PANGO_WEIGHT_ULTRABOLD; break;
+ default: g_assert_not_reached();
+ }
+
+ switch (slant) {
+ case RR_FONTSLANT_NORMAL: pstyle = PANGO_STYLE_NORMAL; break;
+ case RR_FONTSLANT_ITALIC: pstyle = PANGO_STYLE_ITALIC; break;
+ case RR_FONTSLANT_OBLIQUE: pstyle = PANGO_STYLE_OBLIQUE; break;
+ default: g_assert_not_reached();
+ }
+
+ /* setup the font */
+ pango_font_description_set_family(out->font_desc, name);
+ pango_font_description_set_weight(out->font_desc, pweight);
+ pango_font_description_set_style(out->font_desc, pstyle);
+ pango_font_description_set_size(out->font_desc, size * PANGO_SCALE);
+
+ /* setup the layout */
+ pango_layout_set_font_description(out->layout, out->font_desc);
+ pango_layout_set_wrap(out->layout, PANGO_WRAP_WORD_CHAR);
+
+ /* get the ascent and descent */
+ measure_font(inst, out);
+
+ return out;
+}
+
+RrFont *RrFontOpenDefault(const RrInstance *inst)
+{
+ return RrFontOpen(inst, RrDefaultFontFamily, RrDefaultFontSize,
+ RrDefaultFontWeight, RrDefaultFontSlant);
+}
+
+void RrFontRef(RrFont *f)
+{
+ ++f->ref;
+}
+
+void RrFontClose(RrFont *f)
+{
+ if (f) {
+ if (--f->ref < 1) {
+ g_object_unref(f->layout);
+ pango_font_description_free(f->font_desc);
+ g_slice_free(RrFont, f);
+ }
+ }
+}
+
+static void font_measure_full(const RrFont *f, const gchar *str,
+ gint *x, gint *y, gint shadow_x, gint shadow_y,
+ gboolean flow, gint maxwidth)
+{
+ PangoRectangle rect;
+
+ pango_layout_set_text(f->layout, str, -1);
+ if (flow) {
+ pango_layout_set_single_paragraph_mode(f->layout, FALSE);
+ pango_layout_set_width(f->layout, maxwidth * PANGO_SCALE);
+ pango_layout_set_ellipsize(f->layout, PANGO_ELLIPSIZE_NONE);
+ }
+ else {
+ /* single line mode */
+ pango_layout_set_single_paragraph_mode(f->layout, TRUE);
+ pango_layout_set_width(f->layout, -1);
+ pango_layout_set_ellipsize(f->layout, PANGO_ELLIPSIZE_MIDDLE);
+ }
+
+ /* pango_layout_get_pixel_extents lies! this is the right way to get the
+ size of the text's area */
+ pango_layout_get_extents(f->layout, NULL, &rect);
+#if PANGO_VERSION_MAJOR > 1 || \
+ (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
+ /* pass the logical rect as the ink rect, this is on purpose so we get the
+ full area for the text */
+ pango_extents_to_pixels(&rect, NULL);
+#else
+ rect.width = (rect.width + PANGO_SCALE - 1) / PANGO_SCALE;
+ rect.height = (rect.height + PANGO_SCALE - 1) / PANGO_SCALE;
+#endif
+ *x = rect.width + ABS(shadow_x) + 4 /* we put a 2 px edge on each side */;
+ *y = rect.height + ABS(shadow_y);
+}
+
+RrSize *RrFontMeasureString(const RrFont *f, const gchar *str,
+ gint shadow_x, gint shadow_y,
+ gboolean flow, gint maxwidth)
+{
+ RrSize *size;
+
+ g_assert(!flow || maxwidth > 0);
+
+ size = g_slice_new(RrSize);
+ font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y,
+ flow, maxwidth);
+ return size;
+}
+
+gint RrFontHeight(const RrFont *f, gint shadow_y)
+{
+ return (f->ascent + f->descent) / PANGO_SCALE + ABS(shadow_y);
+}
+
+static inline int font_calculate_baseline(RrFont *f, gint height)
+{
+/* For my own reference:
+ * _________
+ * ^space/2 ^height ^baseline
+ * v_________|_ |
+ * | ^ascent | _ _
+ * | | | | |_ _____ _| |_ _ _
+ * | | | | _/ -_) \ / _| || |
+ * | v_________v \__\___/_\_\\__|\_, |
+ * | ^descent |__/
+ * __________|_v
+ * ^space/2 |
+ * V_________v
+ */
+ return (((height * PANGO_SCALE) /* height of the space in pango units */
+ - (f->ascent + f->descent)) /* minus space taken up by text */
+ / 2 /* divided by two -> half of the empty space (this is the top
+ of the text) */
+ + f->ascent) /* now move down to the baseline */
+ / PANGO_SCALE; /* back to pixels */
+}
+
+void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area)
+{
+ gint x,y,w;
+ XftColor c;
+ gint mw;
+ PangoRectangle rect;
+ PangoAttrList *attrlist;
+ PangoEllipsizeMode ell;
+
+ g_assert(!t->flow || t->maxwidth > 0);
+
+ y = area->y;
+ if (!t->flow)
+ /* center the text vertically
+ We do this centering based on the 'baseline' since different fonts
+ have different top edges. It looks bad when the whole string is
+ moved when 1 character from a non-default language is included in
+ the string */
+ y += font_calculate_baseline(t->font, area->height);
+
+ /* the +2 and -4 leave a small blank edge on the sides */
+ x = area->x + 2;
+ w = area->width;
+ if (t->flow) w = MAX(w, t->maxwidth);
+ w -= 4;
+ /* h = area->height; */
+
+ if (t->flow)
+ ell = PANGO_ELLIPSIZE_NONE;
+ else {
+ switch (t->ellipsize) {
+ case RR_ELLIPSIZE_NONE:
+ ell = PANGO_ELLIPSIZE_NONE;
+ break;
+ case RR_ELLIPSIZE_START:
+ ell = PANGO_ELLIPSIZE_START;
+ break;
+ case RR_ELLIPSIZE_MIDDLE:
+ ell = PANGO_ELLIPSIZE_MIDDLE;
+ break;
+ case RR_ELLIPSIZE_END:
+ ell = PANGO_ELLIPSIZE_END;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ pango_layout_set_text(t->font->layout, t->string, -1);
+ pango_layout_set_width(t->font->layout, w * PANGO_SCALE);
+ pango_layout_set_ellipsize(t->font->layout, ell);
+ pango_layout_set_single_paragraph_mode(t->font->layout, !t->flow);
+
+ /* * * end of setting up the layout * * */
+
+ pango_layout_get_pixel_extents(t->font->layout, NULL, &rect);
+ mw = rect.width;
+
+ /* pango_layout_set_alignment doesn't work with
+ pango_xft_render_layout_line */
+ switch (t->justify) {
+ case RR_JUSTIFY_LEFT:
+ break;
+ case RR_JUSTIFY_RIGHT:
+ x += (w - mw);
+ break;
+ case RR_JUSTIFY_CENTER:
+ x += (w - mw) / 2;
+ break;
+ case RR_JUSTIFY_NUM_TYPES:
+ g_assert_not_reached();
+ }
+
+ if (t->shadow_offset_x || t->shadow_offset_y) {
+ /* From nvidia's readme (chapter 23):
+
+ When rendering to a 32-bit window, keep in mind that the X RENDER
+ extension, used by most composite managers, expects "premultiplied
+ alpha" colors. This means that if your color has components (r,g,b)
+ and alpha value a, then you must render (a*r, a*g, a*b, a) into the
+ target window.
+ */
+ c.color.red = (t->shadow_color->r | t->shadow_color->r << 8) *
+ t->shadow_alpha / 255;
+ c.color.green = (t->shadow_color->g | t->shadow_color->g << 8) *
+ t->shadow_alpha / 255;
+ c.color.blue = (t->shadow_color->b | t->shadow_color->b << 8) *
+ t->shadow_alpha / 255;
+ c.color.alpha = 0xffff * t->shadow_alpha / 255;
+ c.pixel = t->shadow_color->pixel;
+
+ /* see below... */
+ if (!t->flow) {
+ pango_xft_render_layout_line
+ (d, &c,
+#if PANGO_VERSION_MAJOR > 1 || \
+ (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
+ pango_layout_get_line_readonly(t->font->layout, 0),
+#else
+ pango_layout_get_line(t->font->layout, 0),
+#endif
+ (x + t->shadow_offset_x) * PANGO_SCALE,
+ (y + t->shadow_offset_y) * PANGO_SCALE);
+ }
+ else {
+ pango_xft_render_layout(d, &c, t->font->layout,
+ (x + t->shadow_offset_x) * PANGO_SCALE,
+ (y + t->shadow_offset_y) * PANGO_SCALE);
+ }
+ }
+
+ c.color.red = t->color->r | t->color->r << 8;
+ c.color.green = t->color->g | t->color->g << 8;
+ c.color.blue = t->color->b | t->color->b << 8;
+ c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */
+ c.pixel = t->color->pixel;
+
+ if (t->shortcut) {
+ const gchar *s = t->string + t->shortcut_pos;
+
+ t->font->shortcut_underline->start_index = t->shortcut_pos;
+ t->font->shortcut_underline->end_index = t->shortcut_pos +
+ (g_utf8_next_char(s) - s);
+
+ /* the attributes are owned by the layout.
+ re-add the attributes to the layout after changing the
+ start and end index */
+ attrlist = pango_layout_get_attributes(t->font->layout);
+ pango_attr_list_ref(attrlist);
+ pango_layout_set_attributes(t->font->layout, attrlist);
+ pango_attr_list_unref(attrlist);
+ }
+
+ /* layout_line() uses y to specify the baseline
+ The line doesn't need to be freed, it's a part of the layout */
+ if (!t->flow) {
+ pango_xft_render_layout_line
+ (d, &c,
+#if PANGO_VERSION_MAJOR > 1 || \
+ (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
+ pango_layout_get_line_readonly(t->font->layout, 0),
+#else
+ pango_layout_get_line(t->font->layout, 0),
+#endif
+ x * PANGO_SCALE,
+ y * PANGO_SCALE);
+ }
+ else {
+ pango_xft_render_layout(d, &c, t->font->layout,
+ x * PANGO_SCALE,
+ y * PANGO_SCALE);
+ }
+
+ if (t->shortcut) {
+ t->font->shortcut_underline->start_index = 0;
+ t->font->shortcut_underline->end_index = 0;
+ /* the attributes are owned by the layout.
+ re-add the attributes to the layout after changing the
+ start and end index */
+ attrlist = pango_layout_get_attributes(t->font->layout);
+ pango_attr_list_ref(attrlist);
+ pango_layout_set_attributes(t->font->layout, attrlist);
+ pango_attr_list_unref(attrlist);
+ }
+}
diff --git a/obrender/font.h b/obrender/font.h
new file mode 100644
index 0000000..07d648d
--- /dev/null
+++ b/obrender/font.h
@@ -0,0 +1,42 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ font.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __font_h
+#define __font_h
+#include "render.h"
+#include "geom.h"
+#include <pango/pango.h>
+
+struct _RrFont {
+ const RrInstance *inst;
+ gint ref;
+ PangoFontDescription *font_desc;
+ PangoLayout *layout; /*!< Used for measuring and rendering strings */
+ PangoAttribute *shortcut_underline; /*< For underlining the shortcut key */
+ gint ascent; /*!< The font's ascent in pango-units */
+ gint descent; /*!< The font's descent in pango-units */
+};
+
+void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *position);
+
+/*! Increment the references for this font, RrFontClose will decrement until 0
+ and then really close it */
+void RrFontRef(RrFont *f);
+
+#endif /* __font_h */
diff --git a/obrender/geom.h b/obrender/geom.h
new file mode 100644
index 0000000..4d81e4b
--- /dev/null
+++ b/obrender/geom.h
@@ -0,0 +1,38 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ geom.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __render_geom_h
+#define __render_geom_h
+
+typedef struct {
+ int width;
+ int height;
+} RrSize;
+
+typedef struct {
+ int x;
+ int y;
+ int width;
+ int height;
+} RrRect;
+
+#define RECT_SET(r, nx, ny, w, h) \
+ (r).x = (nx), (r).y = (ny), (r).width = (w), (r).height = (h)
+
+#endif
diff --git a/obrender/gradient.c b/obrender/gradient.c
new file mode 100644
index 0000000..60a0a55
--- /dev/null
+++ b/obrender/gradient.c
@@ -0,0 +1,836 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ gradient.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2008 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ 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 "render.h"
+#include "gradient.h"
+#include "color.h"
+#include <glib.h>
+#include <string.h>
+
+static void highlight(RrSurface *s, RrPixel32 *x, RrPixel32 *y,
+ gboolean raised);
+static void gradient_parentrelative(RrAppearance *a, gint w, gint h);
+static void gradient_solid(RrAppearance *l, gint w, gint h);
+static void gradient_splitvertical(RrAppearance *a, gint w, gint h);
+static void gradient_vertical(RrSurface *sf, gint w, gint h);
+static void gradient_horizontal(RrSurface *sf, gint w, gint h);
+static void gradient_mirrorhorizontal(RrSurface *sf, gint w, gint h);
+static void gradient_diagonal(RrSurface *sf, gint w, gint h);
+static void gradient_crossdiagonal(RrSurface *sf, gint w, gint h);
+static void gradient_pyramid(RrSurface *sf, gint inw, gint inh);
+
+void RrRender(RrAppearance *a, gint w, gint h)
+{
+ RrPixel32 *data = a->surface.pixel_data;
+ RrPixel32 current;
+ guint r,g,b;
+ register gint off, x;
+
+ switch (a->surface.grad) {
+ case RR_SURFACE_PARENTREL:
+ gradient_parentrelative(a, w, h);
+ break;
+ case RR_SURFACE_SOLID:
+ gradient_solid(a, w, h);
+ break;
+ case RR_SURFACE_SPLIT_VERTICAL:
+ gradient_splitvertical(a, w, h);
+ break;
+ case RR_SURFACE_VERTICAL:
+ gradient_vertical(&a->surface, w, h);
+ break;
+ case RR_SURFACE_HORIZONTAL:
+ gradient_horizontal(&a->surface, w, h);
+ break;
+ case RR_SURFACE_MIRROR_HORIZONTAL:
+ gradient_mirrorhorizontal(&a->surface, w, h);
+ break;
+ case RR_SURFACE_DIAGONAL:
+ gradient_diagonal(&a->surface, w, h);
+ break;
+ case RR_SURFACE_CROSS_DIAGONAL:
+ gradient_crossdiagonal(&a->surface, w, h);
+ break;
+ case RR_SURFACE_PYRAMID:
+ gradient_pyramid(&a->surface, w, h);
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled gradient */
+ return;
+ }
+
+ if (a->surface.interlaced) {
+ gint i;
+ RrPixel32 *p;
+
+ r = a->surface.interlace_color->r;
+ g = a->surface.interlace_color->g;
+ b = a->surface.interlace_color->b;
+ current = (r << RrDefaultRedOffset)
+ + (g << RrDefaultGreenOffset)
+ + (b << RrDefaultBlueOffset);
+ p = data;
+ for (i = 0; i < h; i += 2, p += w)
+ for (x = 0; x < w; ++x, ++p)
+ *p = current;
+ }
+
+ if (a->surface.relief == RR_RELIEF_FLAT && a->surface.border) {
+ r = a->surface.border_color->r;
+ g = a->surface.border_color->g;
+ b = a->surface.border_color->b;
+ current = (r << RrDefaultRedOffset)
+ + (g << RrDefaultGreenOffset)
+ + (b << RrDefaultBlueOffset);
+ for (off = 0, x = 0; x < w; ++x, off++) {
+ *(data + off) = current;
+ *(data + off + ((h-1) * w)) = current;
+ }
+ for (off = 0, x = 0; x < h; ++x, off++) {
+ *(data + (off * w)) = current;
+ *(data + (off * w) + w - 1) = current;
+ }
+ }
+
+ if (a->surface.relief != RR_RELIEF_FLAT) {
+ if (a->surface.bevel == RR_BEVEL_1) {
+ for (off = 1, x = 1; x < w - 1; ++x, off++)
+ highlight(&a->surface, data + off,
+ data + off + (h-1) * w,
+ a->surface.relief==RR_RELIEF_RAISED);
+ for (off = 0, x = 0; x < h; ++x, off++)
+ highlight(&a->surface, data + off * w,
+ data + off * w + w - 1,
+ a->surface.relief==RR_RELIEF_RAISED);
+ }
+
+ if (a->surface.bevel == RR_BEVEL_2) {
+ for (off = 2, x = 2; x < w - 2; ++x, off++)
+ highlight(&a->surface, data + off + w,
+ data + off + (h-2) * w,
+ a->surface.relief==RR_RELIEF_RAISED);
+ for (off = 1, x = 1; x < h-1; ++x, off++)
+ highlight(&a->surface, data + off * w + 1,
+ data + off * w + w - 2,
+ a->surface.relief==RR_RELIEF_RAISED);
+ }
+ }
+}
+
+static void highlight(RrSurface *s, RrPixel32 *x, RrPixel32 *y, gboolean raised)
+{
+ register gint r, g, b;
+
+ RrPixel32 *up, *down;
+ if (raised) {
+ up = x;
+ down = y;
+ } else {
+ up = y;
+ down = x;
+ }
+
+ r = (*up >> RrDefaultRedOffset) & 0xFF;
+ r += (r * s->bevel_light_adjust) >> 8;
+ g = (*up >> RrDefaultGreenOffset) & 0xFF;
+ g += (g * s->bevel_light_adjust) >> 8;
+ b = (*up >> RrDefaultBlueOffset) & 0xFF;
+ b += (b * s->bevel_light_adjust) >> 8;
+ if (r > 0xFF) r = 0xFF;
+ if (g > 0xFF) g = 0xFF;
+ if (b > 0xFF) b = 0xFF;
+ *up = (r << RrDefaultRedOffset) + (g << RrDefaultGreenOffset)
+ + (b << RrDefaultBlueOffset);
+
+ r = (*down >> RrDefaultRedOffset) & 0xFF;
+ r -= (r * s->bevel_dark_adjust) >> 8;
+ g = (*down >> RrDefaultGreenOffset) & 0xFF;
+ g -= (g * s->bevel_dark_adjust) >> 8;
+ b = (*down >> RrDefaultBlueOffset) & 0xFF;
+ b -= (b * s->bevel_dark_adjust) >> 8;
+ *down = (r << RrDefaultRedOffset) + (g << RrDefaultGreenOffset)
+ + (b << RrDefaultBlueOffset);
+}
+
+static void create_bevel_colors(RrAppearance *l)
+{
+ register gint r, g, b;
+
+ /* light color */
+ r = l->surface.primary->r;
+ r += (r * l->surface.bevel_light_adjust) >> 8;
+ g = l->surface.primary->g;
+ g += (g * l->surface.bevel_light_adjust) >> 8;
+ b = l->surface.primary->b;
+ b += (b * l->surface.bevel_light_adjust) >> 8;
+ if (r > 0xFF) r = 0xFF;
+ if (g > 0xFF) g = 0xFF;
+ if (b > 0xFF) b = 0xFF;
+ g_assert(!l->surface.bevel_light);
+ l->surface.bevel_light = RrColorNew(l->inst, r, g, b);
+
+ /* dark color */
+ r = l->surface.primary->r;
+ r -= (r * l->surface.bevel_dark_adjust) >> 8;
+ g = l->surface.primary->g;
+ g -= (g * l->surface.bevel_dark_adjust) >> 8;
+ b = l->surface.primary->b;
+ b -= (b * l->surface.bevel_dark_adjust) >> 8;
+ g_assert(!l->surface.bevel_dark);
+ l->surface.bevel_dark = RrColorNew(l->inst, r, g, b);
+}
+
+/*! Repeat the first pixel over the entire block of memory
+ @param start The block of memory. start[0] will be copied
+ to the rest of the block.
+ @param w The width of the block of memory (including the already-set first
+ element
+*/
+static inline void repeat_pixel(RrPixel32 *start, gint w)
+{
+ register gint x;
+ RrPixel32 *dest;
+
+ dest = start + 1;
+
+ /* for really small things, just copy ourselves */
+ if (w < 8) {
+ for (x = w-1; x > 0; --x)
+ *(dest++) = *start;
+ }
+
+ /* for >= 8, then use O(log n) memcpy's... */
+ else {
+ gchar *cdest;
+ gint lenbytes;
+
+ /* copy the first 3 * 32 bits (3 words) ourselves - then we have
+ 3 + the original 1 = 4 words to make copies of at a time
+
+ this is faster than doing memcpy for 1 or 2 words at a time
+ */
+ for (x = 3; x > 0; --x)
+ *(dest++) = *start;
+
+ /* cdest is a pointer to the pixel data that is typed char* so that
+ adding 1 to its position moves it only one byte
+
+ lenbytes is the amount of bytes that we will be copying each
+ iteration. this doubles each time through the loop.
+
+ x is the number of bytes left to copy into. lenbytes will alwaysa
+ be bounded by x
+
+ this loop will run O(log n) times (n is the number of bytes we
+ need to copy into), since the size of the copy is doubled each
+ iteration. it seems that gcc does some nice optimizations to make
+ this memcpy very fast on hardware with support for vector operations
+ such as mmx or see. here is an idea of the kind of speed up we are
+ getting by doing this (splitvertical3 switches from doing
+ "*(data++) = color" n times to doing this memcpy thing log n times:
+
+ % cumulative self self total
+ time seconds seconds calls ms/call ms/call name
+ 49.44 0.88 0.88 1063 0.83 0.83 splitvertical1
+ 47.19 1.72 0.84 1063 0.79 0.79 splitvertical2
+ 2.81 1.77 0.05 1063 0.05 0.05 splitvertical3
+ */
+ cdest = (gchar*)dest;
+ lenbytes = 4 * sizeof(RrPixel32);
+ for (x = (w - 4) * sizeof(RrPixel32); x > 0;) {
+ memcpy(cdest, start, lenbytes);
+ x -= lenbytes;
+ cdest += lenbytes;
+ lenbytes <<= 1;
+ if (lenbytes > x)
+ lenbytes = x;
+ }
+ }
+}
+
+static void gradient_parentrelative(RrAppearance *a, gint w, gint h)
+{
+ RrPixel32 *source, *dest;
+ gint sw, sh, partial_w, partial_h;
+ register gint i;
+
+ g_assert (a->surface.parent);
+ g_assert (a->surface.parent->w);
+
+ sw = a->surface.parent->w;
+ sh = a->surface.parent->h;
+
+ /* This is a little hack. When a texture is parentrelative, and the same
+ area as the parent, and has a bevel, it will draw its bevel on top
+ of the parent's, amplifying it. So instead, rerender the child with
+ the parent's settings, but the child's bevel and interlace */
+ if (a->surface.relief != RR_RELIEF_FLAT &&
+ (a->surface.parent->surface.relief != RR_RELIEF_FLAT ||
+ a->surface.parent->surface.border) &&
+ !a->surface.parentx && !a->surface.parenty &&
+ sw == w && sh == h)
+ {
+ RrSurface old = a->surface;
+ a->surface = a->surface.parent->surface;
+
+ /* turn these off for the parent */
+ a->surface.relief = RR_RELIEF_FLAT;
+ a->surface.border = FALSE;
+
+ a->surface.pixel_data = old.pixel_data;
+
+ RrRender(a, w, h);
+ a->surface = old;
+ } else {
+ source = (a->surface.parent->surface.pixel_data +
+ a->surface.parentx + sw * a->surface.parenty);
+ dest = a->surface.pixel_data;
+
+ if (a->surface.parentx + w > sw) {
+ partial_w = sw - a->surface.parentx;
+ } else partial_w = w;
+
+ if (a->surface.parenty + h > sh) {
+ partial_h = sh - a->surface.parenty;
+ } else partial_h = h;
+
+ for (i = 0; i < partial_h; i++, source += sw, dest += w) {
+ memcpy(dest, source, partial_w * sizeof(RrPixel32));
+ }
+ }
+}
+
+static void gradient_solid(RrAppearance *l, gint w, gint h)
+{
+ register gint i;
+ RrPixel32 pix;
+ RrPixel32 *data = l->surface.pixel_data;
+ RrSurface *sp = &l->surface;
+ gint left = 0, top = 0, right = w - 1, bottom = h - 1;
+
+ pix = (sp->primary->r << RrDefaultRedOffset)
+ + (sp->primary->g << RrDefaultGreenOffset)
+ + (sp->primary->b << RrDefaultBlueOffset);
+
+ for (i = 0; i < w * h; i++)
+ *data++ = pix;
+
+ if (sp->interlaced)
+ return;
+
+ XFillRectangle(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->primary),
+ 0, 0, w, h);
+
+ switch (sp->relief) {
+ case RR_RELIEF_RAISED:
+ if (!sp->bevel_dark)
+ create_bevel_colors(l);
+
+ switch (sp->bevel) {
+ case RR_BEVEL_1:
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ left, bottom, right, bottom);
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ right, bottom, right, top);
+
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ left, top, right, top);
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ left, bottom, left, top);
+ break;
+ case RR_BEVEL_2:
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ left + 2, bottom - 1, right - 2, bottom - 1);
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ right - 1, bottom - 1, right - 1, top + 1);
+
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ left + 2, top + 1, right - 2, top + 1);
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ left + 1, bottom - 1, left + 1, top + 1);
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled BevelType */
+ }
+ break;
+ case RR_RELIEF_SUNKEN:
+ if (!sp->bevel_dark)
+ create_bevel_colors(l);
+
+ switch (sp->bevel) {
+ case RR_BEVEL_1:
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ left, bottom, right, bottom);
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ right, bottom, right, top);
+
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ left, top, right, top);
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ left, bottom, left, top);
+ break;
+ case RR_BEVEL_2:
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ left + 2, bottom - 1, right - 2, bottom - 1);
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ right - 1, bottom - 1, right - 1, top + 1);
+
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ left + 2, top + 1, right - 2, top + 1);
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ left + 1, bottom - 1, left + 1, top + 1);
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled BevelType */
+ }
+ break;
+ case RR_RELIEF_FLAT:
+ if (sp->border) {
+ XDrawRectangle(RrDisplay(l->inst), l->pixmap,
+ RrColorGC(sp->border_color),
+ left, top, right, bottom);
+ }
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled ReliefType */
+ }
+}
+
+/* * * * * * * * * * * * * * GRADIENT MAGIC WOOT * * * * * * * * * * * * * * */
+
+#define VARS(x) \
+ register gint len##x; \
+ guint color##x[3]; \
+ gint cdelta##x[3], error##x[3] = { 0, 0, 0 }, inc##x[3]; \
+ gboolean bigslope##x[3] /* color slope > 1 */
+
+#define SETUP(x, from, to, w) \
+ len##x = w; \
+ \
+ color##x[0] = from->r; \
+ color##x[1] = from->g; \
+ color##x[2] = from->b; \
+ \
+ cdelta##x[0] = to->r - from->r; \
+ cdelta##x[1] = to->g - from->g; \
+ cdelta##x[2] = to->b - from->b; \
+ \
+ if (cdelta##x[0] < 0) { \
+ cdelta##x[0] = -cdelta##x[0]; \
+ inc##x[0] = -1; \
+ } else \
+ inc##x[0] = 1; \
+ if (cdelta##x[1] < 0) { \
+ cdelta##x[1] = -cdelta##x[1]; \
+ inc##x[1] = -1; \
+ } else \
+ inc##x[1] = 1; \
+ if (cdelta##x[2] < 0) { \
+ cdelta##x[2] = -cdelta##x[2]; \
+ inc##x[2] = -1; \
+ } else \
+ inc##x[2] = 1; \
+ bigslope##x[0] = cdelta##x[0] > w;\
+ bigslope##x[1] = cdelta##x[1] > w;\
+ bigslope##x[2] = cdelta##x[2] > w
+
+#define COLOR_RR(x, c) \
+ c->r = color##x[0]; \
+ c->g = color##x[1]; \
+ c->b = color##x[2]
+
+#define COLOR(x) \
+ ((color##x[0] << RrDefaultRedOffset) + \
+ (color##x[1] << RrDefaultGreenOffset) + \
+ (color##x[2] << RrDefaultBlueOffset))
+
+#define INCREMENT(x, i) \
+ (inc##x[i])
+
+#define NEXT(x) \
+{ \
+ register gint i; \
+ for (i = 2; i >= 0; --i) { \
+ if (!cdelta##x[i]) continue; \
+ \
+ if (!bigslope##x[i]) { \
+ /* Y (color) is dependant on X */ \
+ error##x[i] += cdelta##x[i]; \
+ if ((error##x[i] << 1) >= len##x) { \
+ color##x[i] += INCREMENT(x, i); \
+ error##x[i] -= len##x; \
+ } \
+ } else { \
+ /* X is dependant on Y (color) */ \
+ while (1) { \
+ color##x[i] += INCREMENT(x, i); \
+ error##x[i] += len##x; \
+ if ((error##x[i] << 1) >= cdelta##x[i]) { \
+ error##x[i] -= cdelta##x[i]; \
+ break; \
+ } \
+ } \
+ } \
+ } \
+}
+
+static void gradient_splitvertical(RrAppearance *a, gint w, gint h)
+{
+ register gint y1, y2, y3;
+ RrSurface *sf = &a->surface;
+ RrPixel32 *data;
+ register gint y1sz, y2sz, y3sz;
+
+ VARS(y1);
+ VARS(y2);
+ VARS(y3);
+
+ /* if h <= 5, then a 0 or 1px middle gradient.
+ if h > 5, then always a 1px middle gradient.
+ */
+ if (h <= 5) {
+ y1sz = MAX(h/2, 0);
+ y2sz = (h < 3) ? 0 : (h & 1);
+ y3sz = MAX(h/2, 1);
+ }
+ else {
+ y1sz = h/2 - (1 - (h & 1));
+ y2sz = 1;
+ y3sz = h/2;
+ }
+
+ SETUP(y1, sf->split_primary, sf->primary, y1sz);
+ if (y2sz) {
+ /* setup to get the colors _in between_ these other 2 */
+ SETUP(y2, sf->primary, sf->secondary, y2sz + 2);
+ NEXT(y2); /* skip the first one, its the same as the last of y1 */
+ }
+ SETUP(y3, sf->secondary, sf->split_secondary, y3sz);
+
+ /* find the color for the first pixel of each row first */
+ data = sf->pixel_data;
+
+ for (y1 = y1sz-1; y1 > 0; --y1) {
+ *data = COLOR(y1);
+ data += w;
+ NEXT(y1);
+ }
+ *data = COLOR(y1);
+ data += w;
+ if (y2sz) {
+ for (y2 = y2sz-1; y2 > 0; --y2) {
+ *data = COLOR(y2);
+ data += w;
+ NEXT(y2);
+ }
+ *data = COLOR(y2);
+ data += w;
+ }
+ for (y3 = y3sz-1; y3 > 0; --y3) {
+ *data = COLOR(y3);
+ data += w;
+ NEXT(y3);
+ }
+ *data = COLOR(y3);
+
+ /* copy the first pixels into the whole rows */
+ data = sf->pixel_data;
+ for (y1 = h; y1 > 0; --y1) {
+ repeat_pixel(data, w);
+ data += w;
+ }
+}
+
+static void gradient_horizontal(RrSurface *sf, gint w, gint h)
+{
+ register gint x, y, cpbytes;
+ RrPixel32 *data = sf->pixel_data, *datav;
+ gchar *datac;
+
+ VARS(x);
+ SETUP(x, sf->primary, sf->secondary, w);
+
+ /* set the color values for the first row */
+ datav = data;
+ for (x = w - 1; x > 0; --x) { /* 0 -> w - 1 */
+ *datav = COLOR(x);
+ ++datav;
+ NEXT(x);
+ }
+ *datav = COLOR(x);
+ ++datav;
+
+ /* copy the first row to the rest in O(logn) copies */
+ datac = (gchar*)datav;
+ cpbytes = 1 * w * sizeof(RrPixel32);
+ for (y = (h - 1) * w * sizeof(RrPixel32); y > 0;) {
+ memcpy(datac, data, cpbytes);
+ y -= cpbytes;
+ datac += cpbytes;
+ cpbytes <<= 1;
+ if (cpbytes > y)
+ cpbytes = y;
+ }
+}
+
+static void gradient_mirrorhorizontal(RrSurface *sf, gint w, gint h)
+{
+ register gint x, y, half1, half2, cpbytes;
+ RrPixel32 *data = sf->pixel_data, *datav;
+ gchar *datac;
+
+ VARS(x);
+
+ half1 = (w + 1) / 2;
+ half2 = w / 2;
+
+ /* set the color values for the first row */
+
+ SETUP(x, sf->primary, sf->secondary, half1);
+ datav = data;
+ for (x = half1 - 1; x > 0; --x) { /* 0 -> half1 - 1 */
+ *datav = COLOR(x);
+ ++datav;
+ NEXT(x);
+ }
+ *datav = COLOR(x);
+ ++datav;
+
+ if (half2 > 0) {
+ SETUP(x, sf->secondary, sf->primary, half2);
+ for (x = half2 - 1; x > 0; --x) { /* 0 -> half2 - 1 */
+ *datav = COLOR(x);
+ ++datav;
+ NEXT(x);
+ }
+ *datav = COLOR(x);
+ ++datav;
+ }
+
+ /* copy the first row to the rest in O(logn) copies */
+ datac = (gchar*)datav;
+ cpbytes = 1 * w * sizeof(RrPixel32);
+ for (y = (h - 1) * w * sizeof(RrPixel32); y > 0;) {
+ memcpy(datac, data, cpbytes);
+ y -= cpbytes;
+ datac += cpbytes;
+ cpbytes <<= 1;
+ if (cpbytes > y)
+ cpbytes = y;
+ }
+}
+
+static void gradient_vertical(RrSurface *sf, gint w, gint h)
+{
+ register gint y;
+ RrPixel32 *data;
+
+ VARS(y);
+ SETUP(y, sf->primary, sf->secondary, h);
+
+ /* find the color for the first pixel of each row first */
+ data = sf->pixel_data;
+
+ for (y = h - 1; y > 0; --y) { /* 0 -> h-1 */
+ *data = COLOR(y);
+ data += w;
+ NEXT(y);
+ }
+ *data = COLOR(y);
+
+ /* copy the first pixels into the whole rows */
+ data = sf->pixel_data;
+ for (y = h; y > 0; --y) {
+ repeat_pixel(data, w);
+ data += w;
+ }
+}
+
+static void gradient_diagonal(RrSurface *sf, gint w, gint h)
+{
+ register gint x, y;
+ RrPixel32 *data = sf->pixel_data;
+ RrColor left, right;
+ RrColor extracorner;
+
+ VARS(lefty);
+ VARS(righty);
+ VARS(x);
+
+ extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
+ extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
+ extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
+
+ SETUP(lefty, sf->primary, (&extracorner), h);
+ SETUP(righty, (&extracorner), sf->secondary, h);
+
+ for (y = h - 1; y > 0; --y) { /* 0 -> h-1 */
+ COLOR_RR(lefty, (&left));
+ COLOR_RR(righty, (&right));
+
+ SETUP(x, (&left), (&right), w);
+
+ for (x = w - 1; x > 0; --x) { /* 0 -> w-1 */
+ *(data++) = COLOR(x);
+
+ NEXT(x);
+ }
+ *(data++) = COLOR(x);
+
+ NEXT(lefty);
+ NEXT(righty);
+ }
+ COLOR_RR(lefty, (&left));
+ COLOR_RR(righty, (&right));
+
+ SETUP(x, (&left), (&right), w);
+
+ for (x = w - 1; x > 0; --x) { /* 0 -> w-1 */
+ *(data++) = COLOR(x);
+
+ NEXT(x);
+ }
+ *data = COLOR(x);
+}
+
+static void gradient_crossdiagonal(RrSurface *sf, gint w, gint h)
+{
+ register gint x, y;
+ RrPixel32 *data = sf->pixel_data;
+ RrColor left, right;
+ RrColor extracorner;
+
+ VARS(lefty);
+ VARS(righty);
+ VARS(x);
+
+ extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
+ extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
+ extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
+
+ SETUP(lefty, (&extracorner), sf->secondary, h);
+ SETUP(righty, sf->primary, (&extracorner), h);
+
+ for (y = h - 1; y > 0; --y) { /* 0 -> h-1 */
+ COLOR_RR(lefty, (&left));
+ COLOR_RR(righty, (&right));
+
+ SETUP(x, (&left), (&right), w);
+
+ for (x = w - 1; x > 0; --x) { /* 0 -> w-1 */
+ *(data++) = COLOR(x);
+
+ NEXT(x);
+ }
+ *(data++) = COLOR(x);
+
+ NEXT(lefty);
+ NEXT(righty);
+ }
+ COLOR_RR(lefty, (&left));
+ COLOR_RR(righty, (&right));
+
+ SETUP(x, (&left), (&right), w);
+
+ for (x = w - 1; x > 0; --x) { /* 0 -> w-1 */
+ *(data++) = COLOR(x);
+
+ NEXT(x);
+ }
+ *data = COLOR(x);
+}
+
+static void gradient_pyramid(RrSurface *sf, gint w, gint h)
+{
+ RrPixel32 *ldata, *rdata;
+ RrPixel32 *cp;
+ RrColor left, right;
+ RrColor extracorner;
+ register gint x, y, halfw, halfh, midx, midy;
+
+ VARS(lefty);
+ VARS(righty);
+ VARS(x);
+
+ extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
+ extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
+ extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
+
+ halfw = w >> 1;
+ halfh = h >> 1;
+ midx = w - halfw - halfw; /* 0 or 1, depending if w is even or odd */
+ midy = h - halfh - halfh; /* 0 or 1, depending if h is even or odd */
+
+ SETUP(lefty, sf->primary, (&extracorner), halfh + midy);
+ SETUP(righty, (&extracorner), sf->secondary, halfh + midy);
+
+ /* draw the top half
+
+ it is faster to draw both top quarters together than to draw one and
+ then copy it over to the other side.
+ */
+
+ ldata = sf->pixel_data;
+ rdata = ldata + w - 1;
+ for (y = halfh + midy; y > 0; --y) { /* 0 -> (h+1)/2 */
+ RrPixel32 c;
+
+ COLOR_RR(lefty, (&left));
+ COLOR_RR(righty, (&right));
+
+ SETUP(x, (&left), (&right), halfw + midx);
+
+ for (x = halfw + midx - 1; x > 0; --x) { /* 0 -> (w+1)/2 */
+ c = COLOR(x);
+ *(ldata++) = *(rdata--) = c;
+
+ NEXT(x);
+ }
+ c = COLOR(x);
+ *ldata = *rdata = c;
+ ldata += halfw + 1;
+ rdata += halfw - 1 + midx + w;
+
+ NEXT(lefty);
+ NEXT(righty);
+ }
+
+ /* copy the top half into the bottom half, mirroring it, so we can only
+ copy one row at a time
+
+ it is faster, to move the writing pointer forward, and the reading
+ pointer backward
+
+ this is the current code, moving the write pointer forward and read
+ pointer backward
+ 41.78 4.26 1.78 504 3.53 3.53 gradient_pyramid2
+ this is the opposite, moving the read pointer forward and the write
+ pointer backward
+ 42.27 4.40 1.86 504 3.69 3.69 gradient_pyramid2
+
+ */
+ ldata = sf->pixel_data + (halfh - 1) * w;
+ cp = ldata + (midy + 1) * w;
+ for (y = halfh; y > 0; --y) {
+ memcpy(cp, ldata, w * sizeof(RrPixel32));
+ ldata -= w;
+ cp += w;
+ }
+}
diff --git a/obrender/gradient.h b/obrender/gradient.h
new file mode 100644
index 0000000..8613f0c
--- /dev/null
+++ b/obrender/gradient.h
@@ -0,0 +1,27 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ gradient.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __gradient_h
+#define __gradient_h
+
+#include "render.h"
+
+void RrRender(RrAppearance *a, gint w, gint h);
+
+#endif /* __gradient_h */
diff --git a/obrender/icon.h b/obrender/icon.h
new file mode 100644
index 0000000..f0b2d26
--- /dev/null
+++ b/obrender/icon.h
@@ -0,0 +1,422 @@
+/* GIMP RGBA C-Source image dump (icon.h) */
+
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ icon.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#define OB_DEFAULT_ICON_WIDTH (48)
+#define OB_DEFAULT_ICON_HEIGHT (48)
+#define OB_DEFAULT_ICON_BYTES_PER_PIXEL (4) /* 3:RGB, 4:RGBA */
+#define OB_DEFAULT_ICON_COMMENT \
+ "To recreate this file, save an image as \"C-Source\" in The Gimp. Use \"ob_default_icon\" as the Prefixed Name. Enable Glib Types. Enable Save Alpha Channel. Enable Use Macros instead of Struct."
+#define OB_DEFAULT_ICON_PIXEL_DATA ((guint8*) OB_DEFAULT_ICON_pixel_data)
+static const guint8 OB_DEFAULT_ICON_pixel_data[48 * 48 * 4 + 1] =
+("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\40J\207\15\40J\207\23\40J\207"
+ "\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J"
+ "\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23"
+ "\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207"
+ "\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J"
+ "\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23"
+ "\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207"
+ "\23\40J\207\15\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'P\213\267'Q\214\275'Q\214\275"
+ "'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214"
+ "\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275"
+ "'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275(R\215\275'Q\214\275'Q\214"
+ "\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275"
+ "'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275(R\215"
+ "\275(R\215\275(R\215\275&P\213\267\40J\207\20\0\0\0\0\0\0\0\0\40J\207+Y{\252"
+ "\377\216\253\320\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243"
+ "\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204"
+ "\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377"
+ "\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314"
+ "\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243"
+ "\314\377\204\243\314\377\203\243\314\377\203\243\313\377\203\243\313\377\203"
+ "\243\313\377\203\242\313\377\202\242\313\377\202\242\313\377\202\241\313\377"
+ "\201\241\312\377\201\241\312\377\201\240\312\377\201\240\312\377\200\240\312"
+ "\377\200\240\312\377\200\240\312\377\200\240\312\377\211\247\316\377Jn\241"
+ "\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Wy\251\377]\207\275\377>o\260\377>o"
+ "\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260"
+ "\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377"
+ ">o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260"
+ "\377>o\260\377>o\260\377>o\260\377>o\260\377=o\260\377=o\260\377=o\260\377"
+ "=o\260\377=o\260\377=o\260\377=o\260\377=o\260\377=o\260\377=o\260\377=o\260"
+ "\377=o\260\377_\210\275\377Hm\241\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Tw"
+ "\251\377]\207\276\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r"
+ "\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263"
+ "\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377"
+ "?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263"
+ "\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377"
+ "?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377]\210\277\377Fl\241\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+Rv\250\377\\\210\300\377At\265\377At\265"
+ "\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377"
+ "At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265"
+ "\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377"
+ "At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265"
+ "\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377"
+ "At\265\377\\\210\300\377Dj\240\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Ot\247"
+ "\377\\\211\302\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271"
+ "\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377"
+ "Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271"
+ "\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377"
+ "Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271"
+ "\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377[\211\302\377Bi\240\377\40J\207"
+ "+\0\0\0\0\0\0\0\0\40J\207+Ls\247\377\\\211\303\377Fy\273\377Fy\273\377Fy\273"
+ "\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377"
+ "Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273"
+ "\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377"
+ "Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273"
+ "\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377"
+ "\\\211\303\377Ah\240\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Jq\246\377\\\212"
+ "\305\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277"
+ "\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377"
+ "I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277"
+ "\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377"
+ "I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277"
+ "\377I}\277\377I}\277\377I}\277\377[\212\305\377?g\237\377\40J\207+\0\0\0\0"
+ "\0\0\0\0\40J\207+Jq\246\377\\\212\305\377I}\277\377I}\277\377I}\277\377I}"
+ "\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277"
+ "\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377"
+ "I}\277\377I}\277\377I}\277\377J~\300\377K\177\301\377K\177\301\377K\177\301"
+ "\377K\177\301\377K\177\301\377K\177\301\377K\177\301\377K\177\301\377K\177"
+ "\301\377K\177\301\377K\177\301\377K\177\301\377K\177\301\377K\177\301\377"
+ "K\177\301\377K\177\301\377K\177\301\377K\177\301\377Z\212\307\377=f\237\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+Ip\247\377\\\213\307\377J\177\301\377J\177"
+ "\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377"
+ "J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301"
+ "\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177"
+ "\301\377J\177\301\377J\177\301\377K\200\302\377K\200\302\377K\200\302\377"
+ "K\200\302\377K\200\302\377K\200\302\377K\200\302\377K\200\302\377K\200\302"
+ "\377K\200\302\377K\200\302\377K\200\302\377K\200\302\377K\200\302\377K\200"
+ "\302\377K\200\302\377K\200\302\377K\200\302\377K\200\302\377Z\212\307\377"
+ "=f\237\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Ho\247\377e\223\314\377Z\213\310"
+ "\377Z\213\310\377Z\213\310\377Z\213\310\377Z\213\307\377Z\213\307\377Z\213"
+ "\307\377Z\213\307\377Z\213\307\377Y\212\307\377Y\212\307\377Y\212\307\377"
+ "Y\212\307\377Y\212\307\377X\212\307\377X\212\307\377X\212\307\377X\211\307"
+ "\377X\212\307\377X\212\307\377X\211\307\377X\211\307\377X\211\307\377X\211"
+ "\307\377X\211\307\377X\211\307\377X\211\307\377X\211\307\377X\211\307\377"
+ "X\211\307\377W\211\307\377W\211\307\377W\211\307\377W\211\307\377W\211\307"
+ "\377W\211\307\377V\211\307\377V\211\307\377V\210\307\377V\210\307\377`\217"
+ "\312\377<e\237\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+<b\231\377i\212\267\377"
+ "i\211\266\377i\211\266\377i\211\266\377i\211\266\377i\211\266\377h\211\266"
+ "\377h\211\266\377h\211\266\377h\211\266\377h\211\266\377h\211\266\377h\211"
+ "\266\377g\211\266\377g\211\266\377g\211\266\377g\211\266\377g\210\266\377"
+ "g\210\266\377g\211\266\377g\211\266\377g\210\266\377g\210\266\377g\210\265"
+ "\377g\210\265\377g\210\265\377g\210\265\377g\210\265\377g\210\265\377g\210"
+ "\265\377g\210\265\377g\210\265\377f\210\265\377f\210\264\377f\210\264\377"
+ "f\210\264\377f\210\264\377f\210\264\377f\210\264\377f\210\264\377f\207\264"
+ "\377f\207\264\3778^\226\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+b\177\251\377"
+ "\340\344\351\377\337\342\350\377\337\342\350\377\336\342\350\377\336\342\347"
+ "\377\336\342\347\377\336\342\347\377\336\342\347\377\335\341\347\377\335\341"
+ "\346\377\335\341\346\377\335\341\346\377\335\341\346\377\335\341\346\377\335"
+ "\341\346\377\335\341\346\377\335\341\346\377\334\340\346\377\334\340\346\377"
+ "\335\341\346\377\335\341\346\377\334\340\346\377\334\340\346\377\334\340\345"
+ "\377\334\340\345\377\334\340\345\377\333\337\345\377\333\337\345\377\333\337"
+ "\344\377\333\337\344\377\333\337\344\377\333\336\344\377\332\336\344\377\332"
+ "\336\344\377\332\336\344\377\332\336\343\377\332\336\343\377\332\336\343\377"
+ "\332\336\343\377\332\336\343\377\332\336\343\377\333\337\345\377a}\247\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\204\254\377\355\355\355\377\351\351\351"
+ "\377\351\351\352\377\351\351\351\377\351\351\351\377\351\351\351\377\351\351"
+ "\350\377\351\350\350\377\351\351\351\377\350\350\350\377\350\350\350\377\350"
+ "\350\350\377\350\350\350\377\350\350\350\377\350\350\350\377\350\350\350\377"
+ "\350\347\350\377\350\347\347\377\347\347\347\377\347\347\347\377\347\347\347"
+ "\377\347\347\347\377\347\347\347\377\347\347\347\377\346\347\347\377\347\346"
+ "\347\377\347\346\347\377\346\346\347\377\346\346\346\377\346\346\346\377\346"
+ "\346\346\377\346\346\346\377\346\346\346\377\346\346\346\377\346\346\346\377"
+ "\346\345\345\377\345\345\346\377\345\346\345\377\345\346\345\377\345\346\345"
+ "\377\345\345\345\377\351\351\351\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+h\203\253\377\354\354\354\377\350\350\350\377\347\350\350\377\347"
+ "\347\350\377\347\347\350\377\347\347\347\377\347\347\347\377\347\347\347\377"
+ "\347\347\347\377\347\346\347\377\347\346\346\377\347\346\346\377\347\347\347"
+ "\377\346\346\346\377\346\346\346\377\346\346\346\377\346\346\346\377\346\346"
+ "\346\377\346\346\346\377\345\345\346\377\345\345\345\377\345\345\346\377\345"
+ "\346\346\377\345\345\345\377\345\345\345\377\345\345\345\377\345\345\345\377"
+ "\345\345\345\377\345\344\344\377\345\345\344\377\345\345\345\377\345\344\344"
+ "\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\343\344"
+ "\344\377\343\344\344\377\344\344\344\377\344\344\343\377\344\343\343\377\350"
+ "\350\350\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\353"
+ "\352\352\377\346\346\346\377\346\345\345\377\345\346\346\377\345\346\345\377"
+ "\345\345\345\377\345\345\345\377\345\345\345\377\345\345\346\377\345\345\345"
+ "\377\345\345\345\377\345\345\345\377\345\345\345\377\345\345\345\377\345\345"
+ "\345\377\344\345\345\377\344\344\344\377\344\345\344\377\344\344\344\377\344"
+ "\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\343\343\344\377"
+ "\343\343\343\377\344\343\343\377\343\343\343\377\343\343\343\377\343\343\343"
+ "\377\343\343\343\377\343\343\343\377\342\343\343\377\342\343\343\377\343\342"
+ "\343\377\343\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342"
+ "\342\342\377\342\342\342\377\342\342\342\377\346\346\346\377g\202\252\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\351\351\351\377\345\344\344"
+ "\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\343\344"
+ "\344\377\343\344\344\377\343\343\344\377\343\343\343\377\343\343\343\377\343"
+ "\343\343\377\343\343\343\377\343\343\343\377\343\343\343\377\343\343\343\377"
+ "\343\343\343\377\343\343\343\377\343\342\342\377\342\342\342\377\342\342\342"
+ "\377\342\342\342\377\342\342\342\377\342\341\342\377\342\342\342\377\342\342"
+ "\341\377\342\341\342\377\341\341\342\377\341\341\341\377\341\341\341\377\341"
+ "\341\341\377\341\341\341\377\341\341\341\377\341\340\341\377\341\341\341\377"
+ "\340\341\341\377\341\341\341\377\340\340\340\377\340\340\340\377\340\340\340"
+ "\377\340\340\340\377\346\345\346\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+h\203\253\377\350\350\350\377\342\342\343\377\342\342\342\377\342"
+ "\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377"
+ "\342\342\342\377\342\342\341\377\342\341\342\377\341\341\341\377\341\341\341"
+ "\377\341\341\341\377\341\341\341\377\341\341\341\377\341\341\341\377\341\341"
+ "\341\377\340\341\341\377\341\340\340\377\341\340\340\377\340\340\340\377\340"
+ "\340\340\377\340\340\340\377\340\340\340\377\340\340\340\377\340\340\340\377"
+ "\340\340\337\377\340\340\340\377\337\337\337\377\340\340\337\377\337\337\337"
+ "\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337"
+ "\337\377\337\337\337\377\337\337\336\377\336\337\336\377\336\336\336\377\344"
+ "\344\344\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\347"
+ "\347\347\377\341\341\341\377\341\341\341\377\341\341\341\377\341\341\341\377"
+ "\341\341\341\377\340\341\340\377\340\340\340\377\340\340\340\377\340\340\337"
+ "\377\340\340\340\377\340\340\340\377\340\340\337\377\337\340\337\377\337\340"
+ "\337\377\337\337\340\377\337\337\337\377\337\337\337\377\337\337\337\377\337"
+ "\337\337\377\337\337\337\377\337\337\337\377\336\337\337\377\336\337\336\377"
+ "\337\336\336\377\336\336\336\377\336\336\336\377\336\336\336\377\336\336\336"
+ "\377\336\336\336\377\336\336\336\377\336\336\335\377\336\335\335\377\336\335"
+ "\335\377\336\335\335\377\335\335\335\377\335\335\335\377\335\335\335\377\335"
+ "\335\335\377\335\335\335\377\335\335\335\377\343\343\343\377g\202\252\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\345\346\346\377\337\337\340"
+ "\377\340\337\337\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337"
+ "\337\377\337\337\336\377\337\336\336\377\336\336\337\377\336\336\337\377\336"
+ "\336\336\377\336\336\336\377\336\336\336\377\336\336\335\377\336\336\336\377"
+ "\336\335\336\377\336\336\336\377\335\335\336\377\335\335\335\377\335\335\335"
+ "\377\335\335\335\377\335\335\335\377\335\335\335\377\335\335\335\377\335\335"
+ "\334\377\334\335\335\377\334\334\334\377\334\334\334\377\334\334\334\377\334"
+ "\334\334\377\334\334\334\377\334\334\334\377\334\334\334\377\334\334\334\377"
+ "\334\333\334\377\333\333\333\377\333\333\333\377\333\333\333\377\333\333\333"
+ "\377\333\333\333\377\341\341\342\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+h\203\253\377\345\345\345\377\336\335\336\377\336\335\335\377\335"
+ "\335\335\377\336\335\335\377\335\335\335\377\335\335\335\377\335\335\335\377"
+ "\335\334\335\377\335\335\335\377\335\335\335\377\334\335\334\377\334\334\335"
+ "\377\334\335\334\377\334\334\334\377\334\334\334\377\334\334\334\377\334\334"
+ "\334\377\334\334\334\377\334\334\334\377\334\333\334\377\334\333\333\377\333"
+ "\333\333\377\333\333\333\377\333\333\333\377\333\333\333\377\333\333\333\377"
+ "\333\332\333\377\333\333\333\377\333\333\333\377\332\333\333\377\332\333\332"
+ "\377\332\332\332\377\332\332\332\377\332\332\332\377\332\332\332\377\332\332"
+ "\332\377\332\332\332\377\331\332\331\377\331\332\331\377\331\331\332\377\341"
+ "\341\341\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\343"
+ "\343\344\377\334\334\334\377\334\333\334\377\334\334\334\377\333\333\334\377"
+ "\333\333\333\377\333\334\333\377\333\333\333\377\333\333\333\377\333\333\333"
+ "\377\333\333\333\377\333\333\333\377\333\332\333\377\333\332\333\377\333\332"
+ "\332\377\332\332\333\377\332\332\333\377\332\332\332\377\333\332\332\377\332"
+ "\332\332\377\332\332\332\377\331\332\332\377\331\332\331\377\331\332\331\377"
+ "\331\332\331\377\331\331\331\377\331\331\331\377\332\331\331\377\331\331\331"
+ "\377\331\331\331\377\331\331\331\377\330\330\331\377\330\330\330\377\331\330"
+ "\330\377\330\330\330\377\330\330\330\377\331\330\330\377\330\330\330\377\330"
+ "\330\330\377\330\330\330\377\330\330\330\377\337\337\337\377f\202\252\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\342\342\343\377\332\332\332"
+ "\377\332\332\332\377\332\332\332\377\332\332\332\377\331\332\332\377\331\332"
+ "\332\377\332\332\331\377\332\331\331\377\331\331\331\377\331\331\331\377\331"
+ "\331\331\377\331\331\331\377\331\331\331\377\331\331\331\377\331\331\331\377"
+ "\331\331\331\377\330\330\330\377\331\330\331\377\330\331\330\377\330\330\330"
+ "\377\330\330\330\377\330\330\330\377\330\330\327\377\327\330\330\377\330\330"
+ "\327\377\327\330\330\377\330\330\327\377\327\327\327\377\327\327\327\377\327"
+ "\327\327\377\327\327\327\377\327\327\327\377\327\327\327\377\327\327\327\377"
+ "\326\327\327\377\327\326\326\377\326\327\326\377\326\326\326\377\326\326\326"
+ "\377\326\326\326\377\336\336\337\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+g\203\253\377\341\341\341\377\331\331\331\377\330\331\331\377\330"
+ "\330\330\377\330\330\330\377\330\330\330\377\330\330\330\377\330\330\330\377"
+ "\327\330\330\377\330\330\327\377\330\330\330\377\330\327\330\377\327\327\327"
+ "\377\327\330\327\377\327\330\327\377\327\327\327\377\327\327\327\377\327\327"
+ "\327\377\327\327\327\377\326\327\326\377\326\327\326\377\326\326\326\377\326"
+ "\326\326\377\326\326\326\377\326\326\326\377\326\326\326\377\326\326\326\377"
+ "\326\326\326\377\325\325\326\377\325\326\326\377\326\325\325\377\325\325\325"
+ "\377\325\326\325\377\325\325\325\377\325\325\325\377\325\325\325\377\325\325"
+ "\325\377\325\325\325\377\325\324\325\377\324\324\324\377\324\324\324\377\335"
+ "\335\335\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\340"
+ "\340\340\377\327\327\327\377\327\327\327\377\326\327\327\377\327\326\327\377"
+ "\326\326\326\377\326\326\327\377\326\326\326\377\326\326\326\377\326\326\326"
+ "\377\326\326\326\377\326\326\326\377\326\326\325\377\325\326\325\377\325\326"
+ "\326\377\325\325\325\377\325\325\325\377\325\325\325\377\325\325\325\377\325"
+ "\325\325\377\325\325\325\377\324\325\325\377\325\325\325\377\324\325\325\377"
+ "\324\324\324\377\325\324\324\377\324\324\324\377\324\324\324\377\324\324\324"
+ "\377\323\324\324\377\324\323\324\377\323\324\324\377\323\324\324\377\323\324"
+ "\323\377\323\323\324\377\323\323\323\377\323\323\323\377\323\323\323\377\323"
+ "\323\323\377\323\323\323\377\323\323\323\377\334\334\334\377f\202\252\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\337\336\337\377\325\325\325"
+ "\377\325\325\325\377\325\325\325\377\325\325\325\377\325\325\325\377\325\325"
+ "\325\377\325\325\324\377\325\325\325\377\324\325\324\377\324\324\324\377\324"
+ "\324\324\377\324\324\324\377\324\324\324\377\324\324\324\377\324\324\324\377"
+ "\324\324\324\377\324\323\323\377\323\323\323\377\324\323\323\377\323\323\323"
+ "\377\323\323\323\377\324\323\323\377\323\323\323\377\323\323\323\377\323\322"
+ "\323\377\323\322\322\377\322\322\322\377\322\322\322\377\322\322\322\377\322"
+ "\322\322\377\322\322\322\377\322\322\322\377\322\322\322\377\321\322\321\377"
+ "\321\321\322\377\321\321\321\377\321\321\321\377\321\321\321\377\321\321\321"
+ "\377\321\321\321\377\334\333\334\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+h\203\253\377\336\336\336\377\323\324\323\377\324\323\324\377\324"
+ "\323\323\377\323\324\323\377\323\323\323\377\323\323\323\377\323\323\323\377"
+ "\323\323\323\377\323\323\323\377\323\323\323\377\322\323\323\377\323\322\323"
+ "\377\322\322\323\377\322\322\322\377\322\322\322\377\322\322\322\377\322\322"
+ "\322\377\322\322\322\377\322\322\322\377\322\322\322\377\321\321\321\377\322"
+ "\321\321\377\321\321\321\377\321\321\321\377\321\321\321\377\321\321\321\377"
+ "\321\321\320\377\321\321\321\377\321\321\321\377\321\321\321\377\320\320\320"
+ "\377\320\320\320\377\320\320\320\377\320\320\320\377\320\317\320\377\317\320"
+ "\320\377\320\320\320\377\320\320\320\377\320\320\320\377\320\317\317\377\332"
+ "\332\332\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\335"
+ "\335\335\377\322\322\322\377\322\322\322\377\322\322\322\377\321\322\322\377"
+ "\321\321\321\377\321\321\321\377\322\321\321\377\321\321\321\377\321\321\321"
+ "\377\321\321\321\377\321\321\321\377\321\320\321\377\320\321\320\377\320\320"
+ "\320\377\320\320\320\377\320\320\320\377\320\320\320\377\320\320\320\377\320"
+ "\320\320\377\320\320\320\377\320\320\320\377\320\320\320\377\317\320\320\377"
+ "\317\320\317\377\317\317\317\377\317\317\317\377\317\317\317\377\317\317\317"
+ "\377\317\317\317\377\316\317\317\377\317\317\317\377\317\316\316\377\316\316"
+ "\316\377\317\316\316\377\316\316\316\377\316\316\316\377\316\316\316\377\316"
+ "\316\316\377\316\316\316\377\316\316\316\377\331\331\331\377f\202\252\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\334\334\334\377\320\320\320"
+ "\377\320\320\320\377\320\320\320\377\320\320\320\377\320\320\320\377\320\320"
+ "\320\377\320\320\317\377\317\317\320\377\320\317\317\377\317\317\317\377\317"
+ "\317\317\377\317\317\317\377\317\317\317\377\317\317\317\377\317\317\317\377"
+ "\317\316\317\377\317\317\316\377\316\317\317\377\316\316\316\377\316\316\316"
+ "\377\316\316\316\377\316\316\316\377\316\316\316\377\316\316\316\377\316\315"
+ "\315\377\316\316\315\377\315\316\315\377\315\316\316\377\315\315\315\377\315"
+ "\315\315\377\315\315\315\377\315\315\315\377\315\314\315\377\315\315\315\377"
+ "\315\315\314\377\314\314\314\377\314\314\314\377\314\314\314\377\314\314\314"
+ "\377\314\314\314\377\331\330\331\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+h\203\253\377\333\333\333\377\317\317\316\377\316\317\316\377\317"
+ "\316\316\377\317\316\316\377\316\317\316\377\316\316\316\377\316\316\316\377"
+ "\316\316\316\377\316\316\316\377\316\315\316\377\315\315\315\377\315\315\316"
+ "\377\315\315\315\377\315\315\315\377\315\315\315\377\315\315\315\377\315\315"
+ "\315\377\315\315\315\377\315\315\314\377\315\314\314\377\314\314\314\377\314"
+ "\315\314\377\314\314\314\377\314\314\314\377\314\314\314\377\314\314\314\377"
+ "\314\314\314\377\314\314\313\377\313\313\313\377\314\314\313\377\313\313\313"
+ "\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313"
+ "\313\377\312\313\313\377\312\312\313\377\312\312\313\377\312\312\313\377\327"
+ "\327\327\377g\203\253\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\332"
+ "\332\332\377\315\315\315\377\314\315\315\377\315\315\315\377\315\315\315\377"
+ "\314\315\315\377\314\314\315\377\314\314\314\377\314\314\314\377\314\314\314"
+ "\377\314\314\314\377\314\314\314\377\314\314\314\377\313\314\314\377\313\314"
+ "\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313"
+ "\313\313\377\313\313\313\377\313\312\313\377\313\313\313\377\312\313\313\377"
+ "\313\313\313\377\312\312\312\377\312\312\312\377\312\312\312\377\312\312\312"
+ "\377\312\312\312\377\312\312\312\377\312\312\311\377\312\311\311\377\312\312"
+ "\311\377\311\311\311\377\311\311\311\377\311\311\311\377\311\311\311\377\311"
+ "\311\311\377\311\311\311\377\311\311\311\377\327\326\327\377g\203\253\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\331\331\331\377\313\313\313"
+ "\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313"
+ "\313\377\312\312\313\377\312\313\313\377\312\312\312\377\312\312\312\377\312"
+ "\312\312\377\312\312\312\377\312\312\312\377\312\312\312\377\311\311\311\377"
+ "\312\312\312\377\311\311\311\377\311\311\311\377\311\312\311\377\311\311\311"
+ "\377\311\311\311\377\311\311\311\377\311\311\311\377\311\311\311\377\311\310"
+ "\311\377\310\310\310\377\310\310\310\377\310\310\310\377\310\310\310\377\310"
+ "\310\310\377\310\310\310\377\310\310\310\377\310\310\310\377\310\310\310\377"
+ "\307\307\310\377\310\307\310\377\307\307\307\377\307\307\307\377\307\307\307"
+ "\377\307\307\307\377\325\325\325\377g\203\253\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+h\204\254\377\330\330\331\377\312\312\311\377\312\312\311\377\312"
+ "\311\311\377\311\311\311\377\311\311\311\377\311\311\311\377\310\311\311\377"
+ "\311\311\311\377\311\311\311\377\311\310\311\377\311\310\310\377\311\311\311"
+ "\377\310\310\311\377\310\310\310\377\310\310\310\377\310\310\310\377\310\310"
+ "\310\377\310\310\310\377\307\310\307\377\310\310\310\377\307\307\307\377\307"
+ "\307\310\377\307\307\307\377\307\307\307\377\307\307\307\377\307\307\307\377"
+ "\307\307\307\377\307\307\307\377\306\306\307\377\306\307\307\377\306\306\306"
+ "\377\306\306\306\377\306\306\306\377\306\306\306\377\306\306\306\377\306\306"
+ "\306\377\305\306\306\377\305\306\305\377\305\306\306\377\305\306\306\377\325"
+ "\325\325\377g\203\253\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\204\254\377\327"
+ "\327\327\377\310\310\310\377\310\310\310\377\310\310\310\377\310\307\307\377"
+ "\307\307\310\377\307\307\307\377\307\307\307\377\307\307\307\377\307\307\307"
+ "\377\307\307\307\377\307\307\307\377\307\307\307\377\307\306\306\377\307\306"
+ "\306\377\306\307\306\377\307\306\306\377\306\306\306\377\306\306\306\377\306"
+ "\306\306\377\306\306\306\377\306\306\306\377\306\306\306\377\305\305\305\377"
+ "\305\305\306\377\306\305\305\377\305\305\305\377\305\305\306\377\305\305\305"
+ "\377\305\305\305\377\305\305\305\377\305\304\305\377\304\304\304\377\304\304"
+ "\305\377\304\304\305\377\304\304\304\377\304\304\304\377\304\304\304\377\304"
+ "\304\304\377\304\304\304\377\304\304\304\377\324\324\324\377g\203\253\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\204\254\377\326\326\326\377\307\306\306"
+ "\377\306\306\306\377\306\306\306\377\306\306\306\377\306\306\306\377\306\305"
+ "\306\377\306\306\305\377\305\305\306\377\305\306\306\377\305\305\305\377\305"
+ "\305\305\377\305\305\305\377\305\305\305\377\305\305\305\377\305\305\305\377"
+ "\305\304\305\377\305\305\305\377\304\304\305\377\304\304\304\377\304\304\304"
+ "\377\304\304\304\377\304\304\304\377\304\304\304\377\304\304\304\377\304\304"
+ "\303\377\304\303\304\377\303\303\304\377\303\303\304\377\303\303\303\377\303"
+ "\303\303\377\303\302\303\377\303\303\303\377\303\303\303\377\303\302\303\377"
+ "\303\302\303\377\303\302\303\377\302\302\302\377\302\302\302\377\302\302\302"
+ "\377\302\302\302\377\323\323\323\377h\204\254\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\206+h\204\254\377\325\325\325\377\304\305\305\377\305\305\305\377\305"
+ "\304\304\377\304\304\305\377\304\305\305\377\304\304\304\377\305\304\305\377"
+ "\304\305\304\377\304\304\304\377\304\304\304\377\304\304\303\377\304\304\304"
+ "\377\304\304\303\377\303\303\304\377\303\303\303\377\304\303\303\377\303\303"
+ "\303\377\303\303\303\377\303\303\303\377\303\302\303\377\303\302\303\377\302"
+ "\303\303\377\302\302\303\377\302\303\303\377\302\303\302\377\302\302\302\377"
+ "\302\302\302\377\301\301\301\377\301\301\301\377\301\301\301\377\301\301\301"
+ "\377\301\301\301\377\301\301\301\377\301\301\301\377\301\301\301\377\301\301"
+ "\301\377\301\301\301\377\301\301\301\377\301\301\301\377\301\301\301\377\322"
+ "\322\322\377g\203\253\377\37H\204,\0\0\0\0\0\0\0\1\35Cy0f\202\252\377\350"
+ "\350\350\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377"
+ "\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344"
+ "\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344"
+ "\344\377\344\344\344\377\344\344\344\377\343\343\343\377\343\343\343\377\344"
+ "\344\344\377\344\343\344\377\343\343\343\377\343\343\343\377\343\343\343\377"
+ "\343\343\343\377\343\343\343\377\343\343\343\377\343\343\343\377\342\342\342"
+ "\377\342\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342\342"
+ "\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342"
+ "\342\342\377\342\342\342\377\342\342\342\377\347\347\347\377f\202\252\377"
+ "\33=p3\0\0\0\5\0\0\0\14\27""5`<+T\216\377<d\233\377<d\233\377<d\233\377<d"
+ "\233\377<d\233\377<d\233\377<c\233\377:b\232\377-U\217\377<c\233\377;c\233"
+ "\377:c\233\377:c\233\377:c\233\377:c\233\377:c\233\377:c\232\377:c\232\377"
+ ":b\232\377:c\232\377:c\232\377:b\232\377:b\232\377:b\232\3779b\232\3779b\232"
+ "\3779b\232\3779b\232\3779b\232\3779b\232\3779b\232\3779b\232\3778b\232\377"
+ "+U\217\3778a\231\3778a\232\3778a\232\3778a\232\3778a\232\3778a\232\3778a\232"
+ "\3777`\231\377)R\216\377\25""1YA\0\0\0\23\0\0\0\25\24.UD4_\234\377R\202\277"
+ "\377R\202\277\377R\202\277\377R\202\277\377R\202\277\377R\202\277\377R\202"
+ "\277\377N\177\273\3771^\232\377R\202\277\377Q\202\277\377Q\202\277\377Q\202"
+ "\277\377Q\202\277\377Q\202\277\377Q\202\277\377Q\201\276\377Q\201\276\377"
+ "P\201\276\377Q\201\276\377Q\201\276\377P\201\276\377P\201\276\377P\201\276"
+ "\377P\201\276\377P\201\276\377P\201\276\377P\201\276\377P\201\276\377P\201"
+ "\276\377O\200\276\377O\200\276\377O\200\276\3770]\232\377N\177\274\377O\200"
+ "\276\377O\200\276\377O\200\276\377N\200\276\377N\200\276\377N\200\276\377"
+ "N\177\275\3771]\233\377\22+OI\0\0\0\34\0\0\0\33\20%C@*S\214\377<b\227\377"
+ "<a\227\377<a\227\377<a\227\377<a\227\377<a\227\377<a\227\377;_\226\377-T\216"
+ "\377;`\226\377<a\227\377<a\227\377<a\227\377<a\227\377<a\227\377<a\227\377"
+ "<a\227\377<a\227\377<a\227\377;`\226\377;`\226\377;`\226\377;`\226\377;`\226"
+ "\377;`\226\377;`\226\377;`\226\377;`\226\377;`\226\377;`\226\377;`\226\377"
+ ";`\226\377;`\226\377-T\215\377;`\226\377;`\226\377;`\226\377;`\226\377;a\226"
+ "\377;a\226\377;a\226\377<a\227\377*R\214\377\17!=G\0\0\0#\0\0\0\33\3\7\14"
+ ")\31""6bw\30""4\\}\26""0W\205\25.S\214\25.R\215\25.R\215\25.R\215\25.R\215"
+ "\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25"
+ ".R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25."
+ "R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R"
+ "\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25-Q\217"
+ "\26/T\212\27""2Y\202\30""4^{\2\5\12""1\0\0\0#\0\0\0\25\0\0\0\37\0\0\0+\0\0"
+ "\0""4\0\0\0=\0\0\0C\0\0\0D\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0"
+ "C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0"
+ "\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0"
+ "C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0D\0\0\0F\0\0\0B\0\0\0:\0"
+ "\0\0""2\0\0\0&\0\0\0\35\0\0\0\15\0\0\0\26\0\0\0\40\0\0\0'\0\0\0/\0\0\0""3"
+ "\0\0\0""3\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0"
+ "\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0"
+ "\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0"
+ """2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2"
+ "\0\0\0""2\0\0\0""2\0\0\0""3\0\0\0""5\0\0\0""2\0\0\0,\0\0\0%\0\0\0\34\0\0\0"
+ "\23\0\0\0\1\0\0\0\7\0\0\0\16\0\0\0\25\0\0\0\32\0\0\0\35\0\0\0\35\0\0\0\34"
+ "\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0"
+ "\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0"
+ "\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0"
+ "\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34"
+ "\0\0\0\35\0\0\0\36\0\0\0\35\0\0\0\30\0\0\0\23\0\0\0\12\0\0\0\3\0\0\0\0\0\0"
+ "\0\0\0\0\0\2\0\0\0\5\0\0\0\10\0\0\0\13\0\0\0\14\0\0\0\13\0\0\0\13\0\0\0\13"
+ "\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0"
+ "\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0"
+ "\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0"
+ "\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\14\0\0\0\14"
+ "\0\0\0\12\0\0\0\6\0\0\0\3\0\0\0\0\0\0\0\0");
+
diff --git a/obrender/image.c b/obrender/image.c
new file mode 100644
index 0000000..196d9d1
--- /dev/null
+++ b/obrender/image.c
@@ -0,0 +1,868 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ image.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "geom.h"
+#include "image.h"
+#include "color.h"
+#include "imagecache.h"
+#ifdef USE_IMLIB2
+#include <Imlib2.h>
+#endif
+
+#include <glib.h>
+
+#define FRACTION 12
+#define FLOOR(i) ((i) & (~0UL << FRACTION))
+#define AVERAGE(a, b) (((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b)))
+
+/************************************************************************
+ RrImagePic functions.
+
+ RrImagePics are pictures that are grouped together into RrImageSets. Each
+ RrImagePic in the set has the same logical image inside it, but they are
+ of different sizes. An RrImagePic can be an original (which comes from some
+ outside source, such as an image file), or resized from some other RrImagePic
+ to meet the needs of the user.
+**************************************************************************/
+
+
+/*! Set up an RrImagePic.
+ This does _not_ make a copy of the data. So the value of data must be
+ owned by the caller of this function, and not freed afterward.
+ This function does not allocate an RrImagePic, and can be used for setting
+ up a temporary RrImagePic on the stack. Such an object would then also
+ not be freed with RrImagePicFree.
+*/
+static void RrImagePicInit(RrImagePic *pic, gint w, gint h, RrPixel32 *data)
+{
+ gint i;
+
+ pic->width = w;
+ pic->height = h;
+ pic->data = data;
+ pic->sum = 0;
+ for (i = w*h; i > 0; --i)
+ pic->sum += *(data++);
+}
+
+/*! Create a new RrImagePic from some picture data.
+ This makes a duplicate of the data.
+*/
+static RrImagePic* RrImagePicNew(gint w, gint h, RrPixel32 *data)
+{
+ RrImagePic *pic;
+
+ pic = g_slice_new(RrImagePic);
+ RrImagePicInit(pic, w, h, g_memdup(data, w*h*sizeof(RrPixel32)));
+ return pic;
+}
+
+
+/*! Destroy an RrImagePic.
+ This frees the RrImagePic object and everything inside it.
+*/
+static void RrImagePicFree(RrImagePic *pic)
+{
+ if (pic) {
+ g_free(pic->data);
+ g_slice_free(RrImagePic, pic);
+ }
+}
+
+/************************************************************************
+ RrImageSet functions.
+
+ RrImageSets hold a group of pictures, each of which represent the same logical
+ image, but are physically different sizes.
+ At any time, it may be discovered that two different RrImageSets are actually
+ holding the same logical image. At that time, they would be merged.
+ An RrImageSet holds both original images which come from an outside source,
+ and resized images, which are generated when requests for a specific size are
+ made, and kept around in case they are request again. There is a maximum
+ number of resized images that an RrImageSet will keep around, however.
+
+ Each RrImage points to a single RrImageSet, which keeps track of which
+ RrImages point to it. If two RrImageSets are merged, then the RrImages which
+ pointed to the two RrImageSets will all point at the resulting merged set.
+**************************************************************************/
+
+
+/*! Free an RrImageSet and the stuff inside it.
+ This should only occur when there are no more RrImages pointing to the set.
+*/
+static void RrImageSetFree(RrImageSet *self)
+{
+ GSList *it;
+ gint i;
+
+ if (self) {
+ g_assert(self->images == NULL);
+
+ /* remove all names associated with this RrImageSet */
+ for (it = self->names; it; it = g_slist_next(it)) {
+ g_hash_table_remove(self->cache->name_table, it->data);
+ g_free(it->data);
+ }
+ g_slist_free(self->names);
+
+ /* destroy the RrImagePic objects stored in the RrImageSet. they will
+ be keys in the cache to RrImageSet objects, so remove them from
+ the cache's pic_table as well. */
+ for (i = 0; i < self->n_original; ++i) {
+ g_hash_table_remove(self->cache->pic_table, self->original[i]);
+ RrImagePicFree(self->original[i]);
+ }
+ g_free(self->original);
+ for (i = 0; i < self->n_resized; ++i) {
+ g_hash_table_remove(self->cache->pic_table, self->resized[i]);
+ RrImagePicFree(self->resized[i]);
+ }
+ g_free(self->resized);
+
+ g_slice_free(RrImageSet, self);
+ }
+}
+
+/*! Remove a picture from an RrImageSet as a given position.
+ @param set The RrImageSet to remove the picture from.
+ @param i The index of the picture in the RrImageSet in the list of
+ originals (if @original is TRUE), or in the list of resized pictures (if
+ @original is FALSE).
+ @param original TRUE if the picture is an original, FALSE if it is a resized
+ version of another picture in the RrImageSet.
+ */
+static void RrImageSetRemovePictureAt(RrImageSet *self, gint i,
+ gboolean original)
+{
+ RrImagePic ***list;
+ gint *len;
+
+ if (original) {
+ list = &self->original;
+ len = &self->n_original;
+ }
+ else {
+ list = &self->resized;
+ len = &self->n_resized;
+ }
+
+ g_assert(i >= 0 && i < *len);
+
+ /* remove the picture data as a key in the cache */
+ g_hash_table_remove(self->cache->pic_table, (*list)[i]);
+
+ /* free the picture being removed */
+ RrImagePicFree((*list)[i]);
+
+ /* copy the elements after the removed one in the array forward one space
+ and shrink the array down one size */
+ for (i = i+1; i < *len; ++i)
+ (*list)[i-1] = (*list)[i];
+ --(*len);
+ *list = g_renew(RrImagePic*, *list, *len);
+}
+
+/*! Add an RrImagePic to an RrImageSet.
+ The RrImagePic should _not_ exist in the image cache already.
+ Pictures are added to the front of the list, to maintain the ordering of
+ newest to oldest.
+*/
+static void RrImageSetAddPicture(RrImageSet *self, RrImagePic *pic,
+ gboolean original)
+{
+ gint i;
+ RrImagePic ***list;
+ gint *len;
+
+ g_assert(pic->width > 0 && pic->height > 0);
+ g_assert(g_hash_table_lookup(self->cache->pic_table, pic) == NULL);
+
+ /* choose which list in the RrImageSet to add the new picture to. */
+ if (original) {
+ /* remove the resized picture of the same size if one exists */
+ for (i = 0; i < self->n_resized; ++i)
+ if (self->resized[i]->width == pic->width ||
+ self->resized[i]->height == pic->height)
+ {
+ RrImageSetRemovePictureAt(self, i, FALSE);
+ break;
+ }
+
+ list = &self->original;
+ len = &self->n_original;
+ }
+ else {
+ list = &self->resized;
+ len = &self->n_resized;
+ }
+
+ /* grow the list by one spot, shift everything down one, and insert the new
+ picture at the front of the list */
+ *list = g_renew(RrImagePic*, *list, ++*len);
+ for (i = *len-1; i > 0; --i)
+ (*list)[i] = (*list)[i-1];
+ (*list)[0] = pic;
+
+ /* add the picture as a key to point to this image in the cache */
+ g_hash_table_insert(self->cache->pic_table, (*list)[0], self);
+
+/*
+#ifdef DEBUG
+ g_debug("Adding %s picture to the cache:\n "
+ "Image 0x%lx, w %d h %d Hash %u",
+ (*list == self->original ? "ORIGINAL" : "RESIZED"),
+ (gulong)self, pic->width, pic->height, RrImagePicHash(pic));
+#endif
+*/
+}
+
+/*! Merges two image sets, destroying one, and returning the other. */
+RrImageSet* RrImageSetMergeSets(RrImageSet *b, RrImageSet *a)
+{
+ gint a_i, b_i, merged_i;
+ RrImagePic **original, **resized;
+ gint n_original, n_resized, tmp;
+ GSList *it;
+
+ const gint max_resized = a->cache->max_resized_saved;
+
+ if (!a)
+ return b;
+ if (!b)
+ return a;
+ if (a == b)
+ return b;
+
+ /* the original and resized picture lists in an RrImageSet are kept ordered
+ as newest to oldest. we don't have timestamps for them, so we cannot
+ preserve this in the merged RrImageSet exactly. a decent approximation,
+ i think, is to add them in alternating order (one from a, one from b,
+ repeat). this way, the newest from each will be near the front at
+ least, and in the resized list, when we drop an old picture, we will
+ not always only drop from a or b only, but from each of them equally (or
+ from whichever has more resized pictures.
+ */
+
+ g_assert(b->cache == a->cache);
+
+ a_i = b_i = merged_i = 0;
+ n_original = a->n_original + b->n_original;
+ original = g_new(RrImagePic*, n_original);
+ while (merged_i < n_original) {
+ if (a_i < a->n_original)
+ original[merged_i++] = a->original[a_i++];
+ if (b_i < b->n_original)
+ original[merged_i++] = b->original[b_i++];
+ }
+
+ a_i = b_i = merged_i = 0;
+ n_resized = MIN(max_resized, a->n_resized + b->n_resized);
+ resized = g_new(RrImagePic*, n_resized);
+ while (merged_i < n_resized) {
+ if (a_i < a->n_resized)
+ resized[merged_i++] = a->resized[a_i++];
+ if (b_i < b->n_resized && merged_i < n_resized)
+ resized[merged_i++] = b->resized[b_i++];
+ }
+
+ /* if there are any RrImagePic objects left over in a->resized or
+ b->resized, they need to be disposed of, and removed from the cache.
+
+ updates the size of the list, as we want to remember which pointers
+ were merged from which list (and don't want to remember the ones we
+ did not merge and have freed).
+ */
+ tmp = a_i;
+ for (; a_i < a->n_resized; ++a_i) {
+ g_hash_table_remove(a->cache->pic_table, a->resized[a_i]);
+ RrImagePicFree(a->resized[a_i]);
+ }
+ a->n_resized = tmp;
+
+ tmp = b_i;
+ for (; b_i < b->n_resized; ++b_i) {
+ g_hash_table_remove(a->cache->pic_table, b->resized[b_i]);
+ RrImagePicFree(b->resized[b_i]);
+ }
+ b->n_resized = tmp;
+
+ /* we will use the a object as the merge destination, so things in b will
+ be moving.
+
+ the cache's name_table will point to b for all the names in b->names,
+ so these need to be updated to point at a instead.
+ also, the cache's pic_table will point to b for all the pictures in b,
+ so these need to be updated to point at a as well.
+
+ any RrImage objects that were using b should now use a instead.
+
+ the names and images will be all moved into a, and the merged picture
+ lists will be placed in a. the pictures in a and b are moved to new
+ arrays, so the arrays in a and b need to be freed explicitly (the
+ RrImageSetFree function would free the picture data too which we do not
+ want here). then b can be freed.
+ */
+
+ for (it = b->names; it; it = g_slist_next(it))
+ g_hash_table_insert(a->cache->name_table, it->data, a);
+ for (b_i = 0; b_i < b->n_original; ++b_i)
+ g_hash_table_insert(a->cache->pic_table, b->original[b_i], a);
+ for (b_i = 0; b_i < b->n_resized; ++b_i)
+ g_hash_table_insert(a->cache->pic_table, b->resized[b_i], a);
+
+ for (it = b->images; it; it = g_slist_next(it))
+ ((RrImage*)it->data)->set = a;
+
+ a->images = g_slist_concat(a->images, b->images);
+ b->images = NULL;
+ a->names = g_slist_concat(a->names, b->names);
+ b->names = NULL;
+
+ a->n_original = a->n_resized = 0;
+ g_free(a->original);
+ g_free(a->resized);
+ a->original = a->resized = NULL;
+ b->n_original = b->n_resized = 0;
+ g_free(b->original);
+ g_free(b->resized);
+ b->original = b->resized = NULL;
+
+ a->n_original = n_original;
+ a->original = original;
+ a->n_resized = n_resized;
+ a->resized = resized;
+
+ RrImageSetFree(b);
+
+ return a;
+}
+
+static void RrImageSetAddName(RrImageSet *set, const gchar *name)
+{
+ gchar *n;
+
+ n = g_strdup(name);
+ set->names = g_slist_prepend(set->names, n);
+
+ /* add the new name to the hash table */
+ g_assert(g_hash_table_lookup(set->cache->name_table, n) == NULL);
+ g_hash_table_insert(set->cache->name_table, n, set);
+}
+
+
+/************************************************************************
+ RrImage functions.
+**************************************************************************/
+
+
+void RrImageRef(RrImage *self)
+{
+ ++self->ref;
+}
+
+void RrImageUnref(RrImage *self)
+{
+ if (self && --self->ref == 0) {
+ RrImageSet *set;
+/*
+#ifdef DEBUG
+ g_debug("Refcount to 0, removing ALL pictures from the cache:\n "
+ "Image 0x%lx", (gulong)self);
+#endif
+*/
+ if (self->destroy_func)
+ self->destroy_func(self, self->destroy_data);
+
+ set = self->set;
+ set->images = g_slist_remove(set->images, self);
+
+ /* free the set as well if there are no images pointing to it */
+ if (!set->images)
+ RrImageSetFree(set);
+ g_slice_free(RrImage, self);
+ }
+}
+
+/*! Set function that will be called just before RrImage is destroyed. */
+void RrImageSetDestroyFunc(RrImage *self, RrImageDestroyFunc func,
+ gpointer data)
+{
+ self->destroy_func = func;
+ self->destroy_data = data;
+}
+
+void RrImageAddFromData(RrImage *self, RrPixel32 *data, gint w, gint h)
+{
+ RrImagePic pic, *ppic;
+ RrImageSet *set;
+
+ g_return_if_fail(self != NULL);
+ g_return_if_fail(data != NULL);
+ g_return_if_fail(w > 0 && h > 0);
+
+ RrImagePicInit(&pic, w, h, data);
+ set = g_hash_table_lookup(self->set->cache->pic_table, &pic);
+ if (set)
+ self->set = RrImageSetMergeSets(self->set, set);
+ else {
+ ppic = RrImagePicNew(w, h, data);
+ RrImageSetAddPicture(self->set, ppic, TRUE);
+ }
+}
+
+RrImage* RrImageNewFromData(RrImageCache *cache, RrPixel32 *data,
+ gint w, gint h)
+{
+ RrImagePic pic, *ppic;
+ RrImage *self;
+ RrImageSet *set;
+
+ g_return_val_if_fail(cache != NULL, NULL);
+ g_return_val_if_fail(data != NULL, NULL);
+ g_return_val_if_fail(w > 0 && h > 0, NULL);
+
+ /* finds a picture in the cache, if it is already in there, and use the
+ RrImageSet the picture lives in. */
+ RrImagePicInit(&pic, w, h, data);
+ set = g_hash_table_lookup(cache->pic_table, &pic);
+ if (set) {
+ self = set->images->data; /* just grab any RrImage from the list */
+ RrImageRef(self);
+ return self;
+ }
+
+ /* the image does not exist in any RrImageSet in the cache, so make
+ a new RrImageSet, and a new RrImage that points to it, and place the
+ new image inside the new RrImageSet */
+
+ self = g_slice_new0(RrImage);
+ self->ref = 1;
+ self->set = g_slice_new0(RrImageSet);
+ self->set->cache = cache;
+ self->set->images = g_slist_append(self->set->images, self);
+
+ ppic = RrImagePicNew(w, h, data);
+ RrImageSetAddPicture(self->set, ppic, TRUE);
+
+ return self;
+}
+
+RrImage* RrImageNewFromName(RrImageCache *cache, const gchar *name)
+{
+#ifndef USE_IMLIB2
+ return NULL;
+#else
+ RrImage *self;
+ RrImageSet *set;
+ Imlib_Image img;
+ gint w, h;
+ RrPixel32 *data;
+ gchar *path;
+
+ g_return_val_if_fail(cache != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ set = g_hash_table_lookup(cache->name_table, name);
+ if (set) {
+ self = set->images->data;
+ RrImageRef(self);
+ return self;
+ }
+
+ /* XXX find the path via freedesktop icon spec (use obt) ! */
+ path = g_strdup(name);
+
+ if (!(img = imlib_load_image(path)))
+ g_message("Cannot load image \"%s\" from file \"%s\"", name, path);
+ g_free(path);
+
+ if (!img)
+ return NULL;
+
+ /* Get data and dimensions of the image.
+
+ WARNING: This stuff is NOT threadsafe !!
+ */
+ imlib_context_set_image(img);
+ data = imlib_image_get_data_for_reading_only();
+ w = imlib_image_get_width();
+ h = imlib_image_get_height();
+
+ /* get an RrImage that contains an RrImageSet with this picture in it.
+ the RrImage might be new, or reused if the picture was already in the
+ cache.
+
+ either way, we get back an RrImageSet (via the RrImage), and we must add
+ the name to that RrImageSet. because of the check above, we know that
+ there is no RrImageSet in the cache which already has the given name
+ asosciated with it.
+ */
+
+ self = RrImageNewFromData(cache, data, w, h);
+ RrImageSetAddName(self->set, name);
+
+ imlib_free_image();
+ return self;
+#endif
+}
+
+/************************************************************************
+ Image drawing and resizing operations.
+**************************************************************************/
+
+/*! Given a picture in RGBA format, of a specified size, resize it to the new
+ requested size (but keep its aspect ratio). If the image does not need to
+ be resized (it is already the right size) then this returns NULL. Otherwise
+ it returns a newly allocated RrImagePic with the resized picture inside it
+ @return Returns a newly allocated RrImagePic object with a new version of the
+ image in the requested size (keeping aspect ratio).
+*/
+static RrImagePic* ResizeImage(RrPixel32 *src,
+ gulong srcW, gulong srcH,
+ gulong dstW, gulong dstH)
+{
+ RrPixel32 *dst, *dststart;
+ RrImagePic *pic;
+ gulong dstX, dstY, srcX, srcY;
+ gulong srcX1, srcX2, srcY1, srcY2;
+ gulong ratioX, ratioY;
+ gulong aspectW, aspectH;
+
+ g_assert(srcW > 0);
+ g_assert(srcH > 0);
+ g_assert(dstW > 0);
+ g_assert(dstH > 0);
+
+ /* keep the aspect ratio */
+ aspectW = dstW;
+ aspectH = (gint)(dstW * ((gdouble)srcH / srcW));
+ if (aspectH > dstH) {
+ aspectH = dstH;
+ aspectW = (gint)(dstH * ((gdouble)srcW / srcH));
+ }
+ dstW = aspectW ? aspectW : 1;
+ dstH = aspectH ? aspectH : 1;
+
+ if (srcW == dstW && srcH == dstH)
+ return NULL; /* no scaling needed! */
+
+ dststart = dst = g_new(RrPixel32, dstW * dstH);
+
+ ratioX = (srcW << FRACTION) / dstW;
+ ratioY = (srcH << FRACTION) / dstH;
+
+ srcY2 = 0;
+ for (dstY = 0; dstY < dstH; dstY++) {
+ srcY1 = srcY2;
+ srcY2 += ratioY;
+
+ srcX2 = 0;
+ for (dstX = 0; dstX < dstW; dstX++) {
+ gulong red = 0, green = 0, blue = 0, alpha = 0;
+ gulong portionX, portionY, portionXY, sumXY = 0;
+ RrPixel32 pixel;
+
+ srcX1 = srcX2;
+ srcX2 += ratioX;
+
+ for (srcY = srcY1; srcY < srcY2; srcY += (1UL << FRACTION)) {
+ if (srcY == srcY1) {
+ srcY = FLOOR(srcY);
+ portionY = (1UL << FRACTION) - (srcY1 - srcY);
+ if (portionY > srcY2 - srcY1)
+ portionY = srcY2 - srcY1;
+ }
+ else if (srcY == FLOOR(srcY2))
+ portionY = srcY2 - srcY;
+ else
+ portionY = (1UL << FRACTION);
+
+ for (srcX = srcX1; srcX < srcX2; srcX += (1UL << FRACTION)) {
+ if (srcX == srcX1) {
+ srcX = FLOOR(srcX);
+ portionX = (1UL << FRACTION) - (srcX1 - srcX);
+ if (portionX > srcX2 - srcX1)
+ portionX = srcX2 - srcX1;
+ }
+ else if (srcX == FLOOR(srcX2))
+ portionX = srcX2 - srcX;
+ else
+ portionX = (1UL << FRACTION);
+
+ portionXY = (portionX * portionY) >> FRACTION;
+ sumXY += portionXY;
+
+ pixel = *(src + (srcY >> FRACTION) * srcW
+ + (srcX >> FRACTION));
+ red += ((pixel >> RrDefaultRedOffset) & 0xFF)
+ * portionXY;
+ green += ((pixel >> RrDefaultGreenOffset) & 0xFF)
+ * portionXY;
+ blue += ((pixel >> RrDefaultBlueOffset) & 0xFF)
+ * portionXY;
+ alpha += ((pixel >> RrDefaultAlphaOffset) & 0xFF)
+ * portionXY;
+ }
+ }
+
+ g_assert(sumXY != 0);
+ red /= sumXY;
+ green /= sumXY;
+ blue /= sumXY;
+ alpha /= sumXY;
+
+ *dst++ = (red << RrDefaultRedOffset) |
+ (green << RrDefaultGreenOffset) |
+ (blue << RrDefaultBlueOffset) |
+ (alpha << RrDefaultAlphaOffset);
+ }
+ }
+
+ pic = g_slice_new(RrImagePic);
+ RrImagePicInit(pic, dstW, dstH, dststart);
+
+ return pic;
+}
+
+/*! This draws an RGBA picture into the target, within the rectangle specified
+ by the area parameter. If the area's size differs from the source's then it
+ will be centered within the rectangle */
+void DrawRGBA(RrPixel32 *target, gint target_w, gint target_h,
+ RrPixel32 *source, gint source_w, gint source_h,
+ gint alpha, RrRect *area)
+{
+ RrPixel32 *dest;
+ gint col, num_pixels;
+ gint dw, dh;
+
+ g_assert(source_w <= area->width && source_h <= area->height);
+ g_assert(area->x + area->width <= target_w);
+ g_assert(area->y + area->height <= target_h);
+
+ /* keep the aspect ratio */
+ dw = area->width;
+ dh = (gint)(dw * ((gdouble)source_h / source_w));
+ if (dh > area->height) {
+ dh = area->height;
+ dw = (gint)(dh * ((gdouble)source_w / source_h));
+ }
+
+ /* copy source -> dest, and apply the alpha channel.
+ center the image if it is smaller than the area */
+ col = 0;
+ num_pixels = dw * dh;
+ dest = target + area->x + (area->width - dw) / 2 +
+ (target_w * (area->y + (area->height - dh) / 2));
+ while (num_pixels-- > 0) {
+ guchar a, r, g, b, bgr, bgg, bgb;
+
+ /* apply the rgba's opacity as well */
+ a = ((*source >> RrDefaultAlphaOffset) * alpha) >> 8;
+ r = *source >> RrDefaultRedOffset;
+ g = *source >> RrDefaultGreenOffset;
+ b = *source >> RrDefaultBlueOffset;
+
+ /* background color */
+ bgr = *dest >> RrDefaultRedOffset;
+ bgg = *dest >> RrDefaultGreenOffset;
+ bgb = *dest >> RrDefaultBlueOffset;
+
+ r = bgr + (((r - bgr) * a) >> 8);
+ g = bgg + (((g - bgg) * a) >> 8);
+ b = bgb + (((b - bgb) * a) >> 8);
+
+ *dest = ((r << RrDefaultRedOffset) |
+ (g << RrDefaultGreenOffset) |
+ (b << RrDefaultBlueOffset));
+
+ dest++;
+ source++;
+
+ if (++col >= dw) {
+ col = 0;
+ dest += target_w - dw;
+ }
+ }
+}
+
+/*! Draw an RGBA texture into a target pixel buffer. */
+void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba,
+ gint target_w, gint target_h,
+ RrRect *area)
+{
+ RrImagePic *scaled;
+
+ scaled = ResizeImage(rgba->data, rgba->width, rgba->height,
+ area->width, area->height);
+
+ if (scaled) {
+#ifdef DEBUG
+ g_warning("Scaling an RGBA! You should avoid this and just make "
+ "it the right size yourself!");
+#endif
+ DrawRGBA(target, target_w, target_h,
+ scaled->data, scaled->width, scaled->height,
+ rgba->alpha, area);
+ RrImagePicFree(scaled);
+ }
+ else
+ DrawRGBA(target, target_w, target_h,
+ rgba->data, rgba->width, rgba->height,
+ rgba->alpha, area);
+}
+
+/*! Draw an RrImage texture into a target pixel buffer. If the RrImage does
+ not contain a picture of the appropriate size, then one of its "original"
+ pictures will be resized and used (and stored in the RrImage as a "resized"
+ picture).
+ */
+void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img,
+ gint target_w, gint target_h,
+ RrRect *area)
+{
+ gint i, min_diff, min_i, min_aspect_diff, min_aspect_i;
+ RrImage *self;
+ RrImageSet *set;
+ RrImagePic *pic;
+ gboolean free_pic;
+
+ self = img->image;
+ set = self->set;
+ pic = NULL;
+ free_pic = FALSE;
+
+ /* is there an original of this size? (only the larger of
+ w or h has to be right cuz we maintain aspect ratios) */
+ for (i = 0; i < set->n_original; ++i)
+ if ((set->original[i]->width >= set->original[i]->height &&
+ set->original[i]->width == area->width) ||
+ (set->original[i]->width <= set->original[i]->height &&
+ set->original[i]->height == area->height))
+ {
+ pic = set->original[i];
+ break;
+ }
+
+ /* is there a resize of this size? */
+ for (i = 0; i < set->n_resized; ++i)
+ if ((set->resized[i]->width >= set->resized[i]->height &&
+ set->resized[i]->width == area->width) ||
+ (set->resized[i]->width <= set->resized[i]->height &&
+ set->resized[i]->height == area->height))
+ {
+ gint j;
+ RrImagePic *saved;
+
+ /* save the selected one */
+ saved = set->resized[i];
+
+ /* shift all the others down */
+ for (j = i; j > 0; --j)
+ set->resized[j] = set->resized[j-1];
+
+ /* and move the selected one to the top of the list */
+ set->resized[0] = saved;
+
+ pic = set->resized[0];
+ break;
+ }
+
+ if (!pic) {
+ gdouble aspect;
+ RrImageSet *cache_set;
+
+ /* find an original with a close size */
+ min_diff = min_aspect_diff = -1;
+ min_i = min_aspect_i = 0;
+ aspect = ((gdouble)area->width) / area->height;
+ for (i = 0; i < set->n_original; ++i) {
+ gint diff;
+ gint wdiff, hdiff;
+ gdouble myasp;
+
+ /* our size difference metric.. */
+ wdiff = set->original[i]->width - area->width;
+ if (wdiff < 0) wdiff *= 2; /* prefer scaling down than up */
+ hdiff = set->original[i]->height - area->height;
+ if (hdiff < 0) hdiff *= 2; /* prefer scaling down than up */
+ diff = (wdiff * wdiff) + (hdiff * hdiff);
+
+ /* find the smallest difference */
+ if (min_diff < 0 || diff < min_diff) {
+ min_diff = diff;
+ min_i = i;
+ }
+ /* and also find the smallest difference with the same aspect
+ ratio (and prefer this one) */
+ myasp = ((gdouble)set->original[i]->width) /
+ set->original[i]->height;
+ if (ABS(aspect - myasp) < 0.0000001 &&
+ (min_aspect_diff < 0 || diff < min_aspect_diff))
+ {
+ min_aspect_diff = diff;
+ min_aspect_i = i;
+ }
+ }
+
+ /* use the aspect ratio correct source if there is one */
+ if (min_aspect_i >= 0)
+ min_i = min_aspect_i;
+
+ /* resize the original to the given area */
+ pic = ResizeImage(set->original[min_i]->data,
+ set->original[min_i]->width,
+ set->original[min_i]->height,
+ area->width, area->height);
+
+ /* is it already in the cache ? */
+ cache_set = g_hash_table_lookup(set->cache->pic_table, pic);
+ if (cache_set) {
+ /* merge this set with the one found in the cache - they are
+ apparently the same image ! then next time we won't have to do
+ this resizing, we will use the cache_set's pic instead. */
+ set = RrImageSetMergeSets(set, cache_set);
+ free_pic = TRUE;
+ }
+ else {
+ /* add the resized image to the image, as the first in the resized
+ list */
+ while (set->n_resized >= set->cache->max_resized_saved)
+ /* remove the last one (last used one) to make space for
+ adding our resized picture */
+ RrImageSetRemovePictureAt(set, set->n_resized-1, FALSE);
+ if (set->cache->max_resized_saved)
+ /* add it to the resized list */
+ RrImageSetAddPicture(set, pic, FALSE);
+ else
+ free_pic = TRUE; /* don't leak mem! */
+ }
+ }
+
+ /* The RrImageSet may have changed if we merged it with another, so the
+ RrImage object needs to be updated to use the new merged RrImageSet. */
+ self->set = set;
+
+ g_assert(pic != NULL);
+
+ DrawRGBA(target, target_w, target_h,
+ pic->data, pic->width, pic->height,
+ img->alpha, area);
+ if (free_pic)
+ RrImagePicFree(pic);
+}
diff --git a/obrender/image.h b/obrender/image.h
new file mode 100644
index 0000000..28f29c2
--- /dev/null
+++ b/obrender/image.h
@@ -0,0 +1,32 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ image.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __image_h
+#define __image_h
+
+#include "render.h"
+#include "geom.h"
+
+void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img,
+ gint target_w, gint target_h,
+ RrRect *area);
+void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba,
+ gint target_w, gint target_h,
+ RrRect *area);
+
+#endif
diff --git a/obrender/imagecache.c b/obrender/imagecache.c
new file mode 100644
index 0000000..909d874
--- /dev/null
+++ b/obrender/imagecache.c
@@ -0,0 +1,140 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ imagecache.c for the Openbox window manager
+ Copyright (c) 2008 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "render.h"
+#include "imagecache.h"
+#include "image.h"
+
+static gboolean RrImagePicEqual(const RrImagePic *p1,
+ const RrImagePic *p2);
+
+RrImageCache* RrImageCacheNew(gint max_resized_saved)
+{
+ RrImageCache *self;
+
+ g_assert(max_resized_saved >= 0);
+
+ self = g_slice_new(RrImageCache);
+ self->ref = 1;
+ self->max_resized_saved = max_resized_saved;
+ self->pic_table = g_hash_table_new((GHashFunc)RrImagePicHash,
+ (GEqualFunc)RrImagePicEqual);
+ self->name_table = g_hash_table_new(g_str_hash, g_str_equal);
+ return self;
+}
+
+void RrImageCacheRef(RrImageCache *self)
+{
+ ++self->ref;
+}
+
+void RrImageCacheUnref(RrImageCache *self)
+{
+ if (self && --self->ref == 0) {
+ g_assert(g_hash_table_size(self->pic_table) == 0);
+ g_hash_table_unref(self->pic_table);
+ self->pic_table = NULL;
+
+ g_assert(g_hash_table_size(self->name_table) == 0);
+ g_hash_table_destroy(self->name_table);
+ self->name_table = NULL;
+
+ g_slice_free(RrImageCache, self);
+ }
+}
+
+#define hashsize(n) ((RrPixel32)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+/* mix -- mix 3 32-bit values reversibly. */
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+/* final -- final mixing of 3 32-bit values (a,b,c) into c */
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+/* This is a fast, reversable hash function called "lookup3", found here:
+ http://burtleburtle.net/bob/c/lookup3.c, by Bob Jenkins
+
+ This hashing algorithm is "reversible", that is, not cryptographically
+ secure at all. But we don't care about that, we just want something to
+ tell when images are the same or different relatively quickly.
+*/
+guint32 hashword(const guint32 *key, gint length, guint32 initval)
+{
+ guint32 a,b,c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + (((guint32)length)<<2) + initval;
+
+ /* handle most of the key */
+ while (length > 3)
+ {
+ a += key[0];
+ b += key[1];
+ c += key[2];
+ mix(a,b,c);
+ length -= 3;
+ key += 3;
+ }
+
+ /* handle the last 3 guint32's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3: c+=key[2];
+ case 2: b+=key[1];
+ case 1: a+=key[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /* report the result */
+ return c;
+}
+
+/*! This is some arbitrary initial value for the hashing function. It's
+ constant so that you get the same result from the same data each time.
+*/
+#define HASH_INITVAL 0xf00d
+
+guint RrImagePicHash(const RrImagePic *p)
+{
+ return hashword(p->data, p->width * p->height, HASH_INITVAL);
+}
+
+static gboolean RrImagePicEqual(const RrImagePic *p1,
+ const RrImagePic *p2)
+{
+ return p1->width == p2->width && p1->height == p2->height &&
+ p1->sum == p2->sum;
+}
diff --git a/obrender/imagecache.h b/obrender/imagecache.h
new file mode 100644
index 0000000..f9f5e4d
--- /dev/null
+++ b/obrender/imagecache.h
@@ -0,0 +1,58 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ imagecache.h for the Openbox window manager
+ Copyright (c) 2008 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __imagecache_h
+#define __imagecache_h
+
+#include <glib.h>
+
+struct _RrImagePic;
+
+guint RrImagePicHash(const struct _RrImagePic *p);
+
+/*! Create a new image cache. An image cache is basically a hash table to look
+ up RrImages. Each RrImage in the cache may contain one or more Pictures,
+ that is one or more actual copies of image data at various sizes. For eg,
+ for a window, all of its various icons are loaded into the same RrImage.
+ When an RrImage is drawn and a picture inside it needs to be resized, that
+ is also saved within the RrImage.
+
+ For each picture that an RrImage has, the picture is hashed and that is used
+ as a key to find the RrImage. So, given any picture in any RrImage in the
+ cache, if you hash it, you will find the RrImage.
+*/
+struct _RrImageCache {
+ gint ref;
+ /*! When an original picture is resized for an RrImage, the resized picture
+ is saved in the RrImage. This specifies how many pictures should be
+ saved at a time. When this is exceeded, the least recently used
+ "resized" picture is deleted.
+ */
+ gint max_resized_saved;
+
+ /*! A hash table of image sets in the cache that don't have a file name
+ attached to them, with their key being a hash of the contents of the
+ image. */
+ GHashTable *pic_table;
+
+ /*! Used to find out if an image file has already been loaded into an
+ image set. Provides a quick file_name -> RrImageSet lookup. */
+ GHashTable *name_table;
+};
+
+#endif
diff --git a/obrender/instance.c b/obrender/instance.c
new file mode 100644
index 0000000..af0420a
--- /dev/null
+++ b/obrender/instance.c
@@ -0,0 +1,309 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ instance.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "render.h"
+#include "instance.h"
+
+static RrInstance *definst = NULL;
+
+static void RrTrueColorSetup (RrInstance *inst);
+static void RrPseudoColorSetup (RrInstance *inst);
+
+#ifdef DEBUG
+#include "color.h"
+#endif
+static void
+dest(gpointer data)
+{
+#ifdef DEBUG
+ RrColor *c = data;
+ if (c->refcount > 0)
+ g_error("color %d (%d,%d,%d) in hash table with %d "
+ "leftover references",
+ c->id, RrColorRed(c), RrColorGreen(c), RrColorBlue(c),
+ c->refcount);
+#endif
+}
+
+#if 0
+static void f(gpointer key, gpointer value, gpointer n)
+{
+ RrColor *c = value;
+ if (c->id == *(gint*)n)
+ g_message("color %d has %d references", c->id, c->refcount);
+}
+
+void print_refs(gint id)
+{
+ g_hash_table_foreach(RrColorHash(definst), f, &id);
+}
+#endif
+
+RrInstance* RrInstanceNew (Display *display, gint screen)
+{
+ g_type_init(); /* supposedly needed for pango but seems to work without */
+
+ definst = g_slice_new(RrInstance);
+ definst->display = display;
+ definst->screen = screen;
+
+ definst->depth = DefaultDepth(display, screen);
+ definst->visual = DefaultVisual(display, screen);
+ definst->colormap = DefaultColormap(display, screen);
+ definst->pango = pango_xft_get_context(display, screen);
+
+ definst->pseudo_colors = NULL;
+
+ definst->color_hash = g_hash_table_new_full(g_int_hash, g_int_equal,
+ NULL, dest);
+
+ switch (definst->visual->class) {
+ case TrueColor:
+ RrTrueColorSetup(definst);
+ break;
+ case PseudoColor:
+ case StaticColor:
+ case GrayScale:
+ case StaticGray:
+ RrPseudoColorSetup(definst);
+ break;
+ default:
+ g_critical("Unsupported visual class");
+ g_free (definst);
+ return definst = NULL;
+ }
+ return definst;
+}
+
+static void RrTrueColorSetup (RrInstance *inst)
+{
+ gulong red_mask, green_mask, blue_mask;
+ XImage *timage = NULL;
+
+ timage = XCreateImage(inst->display, inst->visual, inst->depth,
+ ZPixmap, 0, NULL, 1, 1, 32, 0);
+ g_assert(timage != NULL);
+ /* find the offsets for each color in the visual's masks */
+ inst->red_mask = red_mask = timage->red_mask;
+ inst->green_mask = green_mask = timage->green_mask;
+ inst->blue_mask = blue_mask = timage->blue_mask;
+
+ inst->red_offset = 0;
+ inst->green_offset = 0;
+ inst->blue_offset = 0;
+
+ while (! (red_mask & 1)) { inst->red_offset++; red_mask >>= 1; }
+ while (! (green_mask & 1)) { inst->green_offset++; green_mask >>= 1; }
+ while (! (blue_mask & 1)) { inst->blue_offset++; blue_mask >>= 1; }
+
+ inst->red_shift = inst->green_shift = inst->blue_shift = 8;
+ while (red_mask) { red_mask >>= 1; inst->red_shift--; }
+ while (green_mask) { green_mask >>= 1; inst->green_shift--; }
+ while (blue_mask) { blue_mask >>= 1; inst->blue_shift--; }
+ XFree(timage);
+}
+
+#define RrPseudoNcolors(inst) (1 << (inst->pseudo_bpc * 3))
+
+static void RrPseudoColorSetup (RrInstance *inst)
+{
+ XColor icolors[256];
+ gint tr, tg, tb, n, r, g, b, i, incolors, ii;
+ gulong dev;
+ gint cpc, _ncolors;
+
+ /* determine the number of colors and the bits-per-color */
+ inst->pseudo_bpc = 2; /* XXX THIS SHOULD BE A USER OPTION */
+ g_assert(inst->pseudo_bpc >= 1);
+ _ncolors = RrPseudoNcolors(inst);
+
+ if (_ncolors > 1 << inst->depth) {
+ g_message("Invalid colormap size. Resizing.");
+ inst->pseudo_bpc = 1 << (inst->depth/3) >> 3;
+ _ncolors = 1 << (inst->pseudo_bpc * 3);
+ }
+
+ /* build a color cube */
+ inst->pseudo_colors = g_new(XColor, _ncolors);
+ cpc = 1 << inst->pseudo_bpc; /* colors per channel */
+
+ for (n = 0, r = 0; r < cpc; r++)
+ for (g = 0; g < cpc; g++)
+ for (b = 0; b < cpc; b++, n++) {
+ tr = (gint)(((gfloat)(r)/(gfloat)(cpc-1)) * 0xFF);
+ tg = (gint)(((gfloat)(g)/(gfloat)(cpc-1)) * 0xFF);
+ tb = (gint)(((gfloat)(b)/(gfloat)(cpc-1)) * 0xFF);
+ inst->pseudo_colors[n].red = tr | tr << 8;
+ inst->pseudo_colors[n].green = tg | tg << 8;
+ inst->pseudo_colors[n].blue = tb | tb << 8;
+ /* used to track allocation */
+ inst->pseudo_colors[n].flags = DoRed|DoGreen|DoBlue;
+ }
+
+ /* allocate the colors */
+ for (i = 0; i < _ncolors; i++)
+ if (!XAllocColor(inst->display, inst->colormap,
+ &inst->pseudo_colors[i]))
+ inst->pseudo_colors[i].flags = 0; /* mark it as unallocated */
+
+ /* try allocate any colors that failed allocation above */
+
+ /* get the allocated values from the X server
+ (only the first 256 XXX why!?)
+ */
+ incolors = (((1 << inst->depth) > 256) ? 256 : (1 << inst->depth));
+ for (i = 0; i < incolors; i++)
+ icolors[i].pixel = i;
+ XQueryColors(inst->display, inst->colormap, icolors, incolors);
+
+ /* try match unallocated ones */
+ for (i = 0; i < _ncolors; i++) {
+ if (!inst->pseudo_colors[i].flags) { /* if it wasn't allocated... */
+ gulong closest = 0xffffffff, close = 0;
+ for (ii = 0; ii < incolors; ii++) {
+ /* find deviations */
+ r = (inst->pseudo_colors[i].red - icolors[ii].red) & 0xff;
+ g = (inst->pseudo_colors[i].green - icolors[ii].green) & 0xff;
+ b = (inst->pseudo_colors[i].blue - icolors[ii].blue) & 0xff;
+ /* find a weighted absolute deviation */
+ dev = (r * r) + (g * g) + (b * b);
+
+ if (dev < closest) {
+ closest = dev;
+ close = ii;
+ }
+ }
+
+ inst->pseudo_colors[i].red = icolors[close].red;
+ inst->pseudo_colors[i].green = icolors[close].green;
+ inst->pseudo_colors[i].blue = icolors[close].blue;
+ inst->pseudo_colors[i].pixel = icolors[close].pixel;
+
+ /* try alloc this closest color, it had better succeed! */
+ if (XAllocColor(inst->display, inst->colormap,
+ &inst->pseudo_colors[i]))
+ /* mark as alloced */
+ inst->pseudo_colors[i].flags = DoRed|DoGreen|DoBlue;
+ else
+ /* wtf has gone wrong, its already alloced for chissake! */
+ g_assert_not_reached();
+ }
+ }
+}
+
+void RrInstanceFree (RrInstance *inst)
+{
+ if (inst) {
+ if (inst == definst) definst = NULL;
+ g_free(inst->pseudo_colors);
+ g_hash_table_destroy(inst->color_hash);
+ g_object_unref(inst->pango);
+ g_slice_free(RrInstance, inst);
+ }
+}
+
+Display* RrDisplay (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->display;
+}
+
+gint RrScreen (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->screen;
+}
+
+Window RrRootWindow (const RrInstance *inst)
+{
+ return RootWindow (RrDisplay (inst), RrScreen (inst));
+}
+
+Visual *RrVisual (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->visual;
+}
+
+gint RrDepth (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->depth;
+}
+
+Colormap RrColormap (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->colormap;
+}
+
+gint RrRedOffset (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->red_offset;
+}
+
+gint RrGreenOffset (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->green_offset;
+}
+
+gint RrBlueOffset (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->blue_offset;
+}
+
+gint RrRedShift (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->red_shift;
+}
+
+gint RrGreenShift (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->green_shift;
+}
+
+gint RrBlueShift (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->blue_shift;
+}
+
+gint RrRedMask (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->red_mask;
+}
+
+gint RrGreenMask (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->green_mask;
+}
+
+gint RrBlueMask (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->blue_mask;
+}
+
+guint RrPseudoBPC (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->pseudo_bpc;
+}
+
+XColor *RrPseudoColors (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->pseudo_colors;
+}
+
+GHashTable* RrColorHash (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->color_hash;
+}
diff --git a/obrender/instance.h b/obrender/instance.h
new file mode 100644
index 0000000..324f061
--- /dev/null
+++ b/obrender/instance.h
@@ -0,0 +1,57 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ instance.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __render_instance_h
+#define __render_instance_h
+
+#include <X11/Xlib.h>
+#include <glib.h>
+#include <pango/pangoxft.h>
+
+struct _RrInstance {
+ Display *display;
+ gint screen;
+
+ Visual *visual;
+ gint depth;
+ Colormap colormap;
+ PangoContext *pango;
+
+ gint red_offset;
+ gint green_offset;
+ gint blue_offset;
+
+ gint red_shift;
+ gint green_shift;
+ gint blue_shift;
+
+ gint red_mask;
+ gint green_mask;
+ gint blue_mask;
+
+ gint pseudo_bpc;
+ XColor *pseudo_colors;
+
+ GHashTable *color_hash;
+};
+
+guint RrPseudoBPC (const RrInstance *inst);
+XColor* RrPseudoColors (const RrInstance *inst);
+GHashTable* RrColorHash (const RrInstance *inst);
+
+#endif
diff --git a/obrender/mask.c b/obrender/mask.c
new file mode 100644
index 0000000..506d5b2
--- /dev/null
+++ b/obrender/mask.c
@@ -0,0 +1,82 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ mask.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ 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 "render.h"
+#include "color.h"
+#include "mask.h"
+
+RrPixmapMask *RrPixmapMaskNew(const RrInstance *inst,
+ gint w, gint h, const gchar *data)
+{
+ RrPixmapMask *m = g_slice_new(RrPixmapMask);
+ m->inst = inst;
+ m->width = w;
+ m->height = h;
+ /* round up to nearest byte */
+ m->data = g_memdup(data, (w + 7) / 8 * h);
+ m->mask = XCreateBitmapFromData(RrDisplay(inst), RrRootWindow(inst),
+ data, w, h);
+ return m;
+}
+
+void RrPixmapMaskFree(RrPixmapMask *m)
+{
+ if (m) {
+ XFreePixmap(RrDisplay(m->inst), m->mask);
+ g_free(m->data);
+ g_slice_free(RrPixmapMask, m);
+ }
+}
+
+void RrPixmapMaskDraw(Pixmap p, const RrTextureMask *m, const RrRect *area)
+{
+ gint x, y;
+ if (m->mask == NULL) return; /* no mask given */
+
+ /* set the clip region */
+ x = area->x + (area->width - m->mask->width) / 2;
+ y = area->y + (area->height - m->mask->height) / 2;
+
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+
+ XSetClipMask(RrDisplay(m->mask->inst), RrColorGC(m->color), m->mask->mask);
+ XSetClipOrigin(RrDisplay(m->mask->inst), RrColorGC(m->color), x, y);
+
+ /* fill in the clipped region */
+ XFillRectangle(RrDisplay(m->mask->inst), p, RrColorGC(m->color), x, y,
+ x + m->mask->width, y + m->mask->height);
+
+ /* unset the clip region */
+ XSetClipMask(RrDisplay(m->mask->inst), RrColorGC(m->color), None);
+ XSetClipOrigin(RrDisplay(m->mask->inst), RrColorGC(m->color), 0, 0);
+}
+
+RrPixmapMask *RrPixmapMaskCopy(const RrPixmapMask *src)
+{
+ RrPixmapMask *m = g_slice_new(RrPixmapMask);
+ m->inst = src->inst;
+ m->width = src->width;
+ m->height = src->height;
+ /* round up to nearest byte */
+ m->data = g_memdup(src->data, (src->width + 7) / 8 * src->height);
+ m->mask = XCreateBitmapFromData(RrDisplay(m->inst), RrRootWindow(m->inst),
+ m->data, m->width, m->height);
+ return m;
+}
diff --git a/obrender/mask.h b/obrender/mask.h
new file mode 100644
index 0000000..4dc8503
--- /dev/null
+++ b/obrender/mask.h
@@ -0,0 +1,32 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ mask.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __mask_h
+#define __mask_h
+
+#include "render.h"
+#include "geom.h"
+
+RrPixmapMask *RrPixmapMaskNew(const RrInstance *inst,
+ gint w, gint h, const gchar *data);
+void RrPixmapMaskFree(RrPixmapMask *m);
+RrPixmapMask *RrPixmapMaskCopy(const RrPixmapMask *src);
+void RrPixmapMaskDraw(Pixmap p, const RrTextureMask *m, const RrRect *area);
+
+#endif
diff --git a/obrender/obrender-3.5.pc.in b/obrender/obrender-3.5.pc.in
new file mode 100644
index 0000000..2c8a435
--- /dev/null
+++ b/obrender/obrender-3.5.pc.in
@@ -0,0 +1,14 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+xcflags=@X_CFLAGS@
+xlibs=@X_LIBS@
+
+Name: ObRender
+Description: Openbox Render Library
+Version: @RR_VERSION@
+Requires: obt-3.5 glib-2.0 xft pangoxft @PKG_CONFIG_IMLIB@
+Libs: -L${libdir} -lobrender ${xlibs}
+Cflags: -I${includedir}/openbox/@RR_VERSION@ ${xcflags}
diff --git a/obrender/render.c b/obrender/render.c
new file mode 100644
index 0000000..fe9a2a8
--- /dev/null
+++ b/obrender/render.c
@@ -0,0 +1,575 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ render.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ 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 "render.h"
+#include "gradient.h"
+#include "font.h"
+#include "mask.h"
+#include "color.h"
+#include "image.h"
+#include "theme.h"
+
+#include <glib.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xft/Xft.h>
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+static void pixel_data_to_pixmap(RrAppearance *l,
+ gint x, gint y, gint w, gint h);
+
+Pixmap RrPaintPixmap(RrAppearance *a, gint w, gint h)
+{
+ gint i, transferred = 0, force_transfer = 0;
+ Pixmap oldp = None;
+ RrRect tarea; /* area in which to draw textures */
+ gboolean resized;
+
+ if (w <= 0 || h <= 0) return None;
+
+ if (a->surface.parentx < 0 || a->surface.parenty < 0) {
+ /* ob_debug("Invalid parent co-ordinates\n"); */
+ return None;
+ }
+
+ if (a->surface.grad == RR_SURFACE_PARENTREL &&
+ (a->surface.parentx >= a->surface.parent->w ||
+ a->surface.parenty >= a->surface.parent->h))
+ {
+ return None;
+ }
+
+ resized = (a->w != w || a->h != h);
+
+ oldp = a->pixmap; /* save to free after changing the visible pixmap */
+ a->pixmap = XCreatePixmap(RrDisplay(a->inst),
+ RrRootWindow(a->inst),
+ w, h, RrDepth(a->inst));
+
+ g_assert(a->pixmap != None);
+ a->w = w;
+ a->h = h;
+
+ if (a->xftdraw != NULL)
+ XftDrawDestroy(a->xftdraw);
+ a->xftdraw = XftDrawCreate(RrDisplay(a->inst), a->pixmap,
+ RrVisual(a->inst), RrColormap(a->inst));
+ g_assert(a->xftdraw != NULL);
+
+ if (resized) {
+ g_free(a->surface.pixel_data);
+ a->surface.pixel_data = g_new(RrPixel32, w * h);
+ }
+
+ RrRender(a, w, h);
+
+ {
+ gint l, t, r, b;
+ RrMargins(a, &l, &t, &r, &b);
+ RECT_SET(tarea, l, t, w - l - r, h - t - b);
+ }
+
+ for (i = 0; i < a->textures; i++) {
+ switch (a->texture[i].type) {
+ case RR_TEXTURE_NONE:
+ break;
+ case RR_TEXTURE_TEXT:
+ if (!transferred) {
+ transferred = 1;
+ if ((a->surface.grad != RR_SURFACE_SOLID)
+ || (a->surface.interlaced))
+ pixel_data_to_pixmap(a, 0, 0, w, h);
+ }
+ if (a->xftdraw == NULL) {
+ a->xftdraw = XftDrawCreate(RrDisplay(a->inst), a->pixmap,
+ RrVisual(a->inst),
+ RrColormap(a->inst));
+ }
+ RrFontDraw(a->xftdraw, &a->texture[i].data.text, &tarea);
+ break;
+ case RR_TEXTURE_LINE_ART:
+ if (!transferred) {
+ transferred = 1;
+ if ((a->surface.grad != RR_SURFACE_SOLID)
+ || (a->surface.interlaced))
+ pixel_data_to_pixmap(a, 0, 0, w, h);
+ }
+ XDrawLine(RrDisplay(a->inst), a->pixmap,
+ RrColorGC(a->texture[i].data.lineart.color),
+ a->texture[i].data.lineart.x1,
+ a->texture[i].data.lineart.y1,
+ a->texture[i].data.lineart.x2,
+ a->texture[i].data.lineart.y2);
+ break;
+ case RR_TEXTURE_MASK:
+ if (!transferred) {
+ transferred = 1;
+ if ((a->surface.grad != RR_SURFACE_SOLID)
+ || (a->surface.interlaced))
+ pixel_data_to_pixmap(a, 0, 0, w, h);
+ }
+ RrPixmapMaskDraw(a->pixmap, &a->texture[i].data.mask, &tarea);
+ break;
+ case RR_TEXTURE_IMAGE:
+ g_assert(!transferred);
+ {
+ RrRect narea = tarea;
+ RrTextureImage *img = &a->texture[i].data.image;
+ narea.x += img->tx;
+ narea.width -= img->tx;
+ narea.y += img->ty;
+ narea.height -= img->ty;
+ if (img->twidth)
+ narea.width = MIN(narea.width, img->twidth);
+ if (img->theight)
+ narea.height = MIN(narea.height, img->theight);
+ RrImageDrawImage(a->surface.pixel_data,
+ &a->texture[i].data.image,
+ a->w, a->h,
+ &narea);
+ }
+ force_transfer = 1;
+ break;
+ case RR_TEXTURE_RGBA:
+ g_assert(!transferred);
+ {
+ RrRect narea = tarea;
+ RrTextureRGBA *rgb = &a->texture[i].data.rgba;
+ narea.x += rgb->tx;
+ narea.width -= rgb->tx;
+ narea.y += rgb->ty;
+ narea.height -= rgb->ty;
+ if (rgb->twidth)
+ narea.width = MIN(narea.width, rgb->twidth);
+ if (rgb->theight)
+ narea.height = MIN(narea.height, rgb->theight);
+ RrImageDrawRGBA(a->surface.pixel_data,
+ &a->texture[i].data.rgba,
+ a->w, a->h,
+ &narea);
+ }
+ force_transfer = 1;
+ break;
+ case RR_TEXTURE_NUM_TYPES:
+ g_assert_not_reached();
+ }
+ }
+
+ if (!transferred) {
+ transferred = 1;
+ if ((a->surface.grad != RR_SURFACE_SOLID) || (a->surface.interlaced) ||
+ force_transfer)
+ {
+ pixel_data_to_pixmap(a, 0, 0, w, h);
+ }
+ }
+
+ return oldp;
+}
+
+void RrPaint(RrAppearance *a, Window win, gint w, gint h)
+{
+ Pixmap oldp;
+
+ oldp = RrPaintPixmap(a, w, h);
+ XSetWindowBackgroundPixmap(RrDisplay(a->inst), win, a->pixmap);
+ XClearWindow(RrDisplay(a->inst), win);
+ /* free this after changing the visible pixmap */
+ if (oldp) XFreePixmap(RrDisplay(a->inst), oldp);
+}
+
+RrAppearance *RrAppearanceNew(const RrInstance *inst, gint numtex)
+{
+ RrAppearance *out;
+
+ out = g_slice_new0(RrAppearance);
+ out->inst = inst;
+ out->textures = numtex;
+ out->surface.bevel_light_adjust = 128;
+ out->surface.bevel_dark_adjust = 64;
+ if (numtex) out->texture = g_new0(RrTexture, numtex);
+
+ return out;
+}
+
+void RrAppearanceRemoveTextures(RrAppearance *a)
+{
+ g_free(a->texture);
+ a->textures = 0;
+}
+
+void RrAppearanceAddTextures(RrAppearance *a, gint numtex)
+{
+ g_assert(a->textures == 0);
+
+ a->textures = numtex;
+ if (numtex) a->texture = g_new0(RrTexture, numtex);
+}
+
+void RrAppearanceClearTextures(RrAppearance *a)
+{
+ memset(a->texture, 0, a->textures * sizeof(RrTexture));
+}
+
+/* deep copy of orig, means reset ref to 1 on copy
+ * and copy each thing memwise. */
+RrAppearance *RrAppearanceCopy(RrAppearance *orig)
+{
+ RrSurface *spo, *spc;
+ RrAppearance *copy = g_slice_new(RrAppearance);
+
+ copy->inst = orig->inst;
+
+ spo = &(orig->surface);
+ spc = &(copy->surface);
+ spc->grad = spo->grad;
+ spc->relief = spo->relief;
+ spc->bevel = spo->bevel;
+ if (spo->primary != NULL)
+ spc->primary = RrColorNew(copy->inst,
+ spo->primary->r,
+ spo->primary->g,
+ spo->primary->b);
+ else spc->primary = NULL;
+
+ if (spo->secondary != NULL)
+ spc->secondary = RrColorNew(copy->inst,
+ spo->secondary->r,
+ spo->secondary->g,
+ spo->secondary->b);
+ else spc->secondary = NULL;
+
+ if (spo->border_color != NULL)
+ spc->border_color = RrColorNew(copy->inst,
+ spo->border_color->r,
+ spo->border_color->g,
+ spo->border_color->b);
+ else spc->border_color = NULL;
+
+ if (spo->interlace_color != NULL)
+ spc->interlace_color = RrColorNew(copy->inst,
+ spo->interlace_color->r,
+ spo->interlace_color->g,
+ spo->interlace_color->b);
+ else spc->interlace_color = NULL;
+
+ if (spo->bevel_dark != NULL)
+ spc->bevel_dark = RrColorNew(copy->inst,
+ spo->bevel_dark->r,
+ spo->bevel_dark->g,
+ spo->bevel_dark->b);
+ else spc->bevel_dark = NULL;
+
+ if (spo->bevel_light != NULL)
+ spc->bevel_light = RrColorNew(copy->inst,
+ spo->bevel_light->r,
+ spo->bevel_light->g,
+ spo->bevel_light->b);
+ else spc->bevel_light = NULL;
+
+ if (spo->split_primary != NULL)
+ spc->split_primary = RrColorNew(copy->inst,
+ spo->split_primary->r,
+ spo->split_primary->g,
+ spo->split_primary->b);
+ else spc->split_primary = NULL;
+
+ if (spo->split_secondary != NULL)
+ spc->split_secondary = RrColorNew(copy->inst,
+ spo->split_secondary->r,
+ spo->split_secondary->g,
+ spo->split_secondary->b);
+ else spc->split_secondary = NULL;
+
+ spc->interlaced = spo->interlaced;
+ spc->bevel_light_adjust = spo->bevel_light_adjust;
+ spc->bevel_dark_adjust = spo->bevel_dark_adjust;
+ spc->border = spo->border;
+ spc->parent = NULL;
+ spc->parentx = spc->parenty = 0;
+ spc->pixel_data = NULL;
+
+ copy->textures = orig->textures;
+ copy->texture = g_memdup(orig->texture,
+ orig->textures * sizeof(RrTexture));
+ copy->pixmap = None;
+ copy->xftdraw = NULL;
+ copy->w = copy->h = 0;
+ return copy;
+}
+
+/* now decrements ref counter, and frees only if ref <= 0 */
+void RrAppearanceFree(RrAppearance *a)
+{
+ if (a) {
+ RrSurface *p;
+ if (a->pixmap != None) XFreePixmap(RrDisplay(a->inst), a->pixmap);
+ if (a->xftdraw != NULL) XftDrawDestroy(a->xftdraw);
+ if (a->textures)
+ g_free(a->texture);
+ p = &a->surface;
+ RrColorFree(p->primary);
+ RrColorFree(p->secondary);
+ RrColorFree(p->border_color);
+ RrColorFree(p->interlace_color);
+ RrColorFree(p->bevel_dark);
+ RrColorFree(p->bevel_light);
+ RrColorFree(p->split_primary);
+ RrColorFree(p->split_secondary);
+ g_free(p->pixel_data);
+ p->pixel_data = NULL;
+ g_slice_free(RrAppearance, a);
+ }
+}
+
+static void pixel_data_to_pixmap(RrAppearance *l,
+ gint x, gint y, gint w, gint h)
+{
+ RrPixel32 *in, *scratch;
+ Pixmap out;
+ XImage *im = NULL;
+ im = XCreateImage(RrDisplay(l->inst), RrVisual(l->inst), RrDepth(l->inst),
+ ZPixmap, 0, NULL, w, h, 32, 0);
+ g_assert(im != NULL);
+
+ in = l->surface.pixel_data;
+ out = l->pixmap;
+
+/* this malloc is a complete waste of time on normal 32bpp
+ as reduce_depth just sets im->data = data and returns
+*/
+ scratch = g_new(RrPixel32, im->width * im->height);
+ im->data = (gchar*) scratch;
+ RrReduceDepth(l->inst, in, im);
+ XPutImage(RrDisplay(l->inst), out,
+ DefaultGC(RrDisplay(l->inst), RrScreen(l->inst)),
+ im, 0, 0, x, y, w, h);
+ im->data = NULL;
+ XDestroyImage(im);
+ g_free(scratch);
+}
+
+void RrMargins (RrAppearance *a, gint *l, gint *t, gint *r, gint *b)
+{
+ *l = *t = *r = *b = 0;
+
+ if (a->surface.grad != RR_SURFACE_PARENTREL) {
+ if (a->surface.relief != RR_RELIEF_FLAT) {
+ switch (a->surface.bevel) {
+ case RR_BEVEL_1:
+ *l = *t = *r = *b = 1;
+ break;
+ case RR_BEVEL_2:
+ *l = *t = *r = *b = 2;
+ break;
+ case RR_BEVEL_NUM_TYPES:
+ g_assert_not_reached();
+ }
+ } else if (a->surface.border) {
+ *l = *t = *r = *b = 1;
+ }
+ }
+}
+
+void RrMinSize(RrAppearance *a, gint *w, gint *h)
+{
+ *w = RrMinWidth(a);
+ *h = RrMinHeight(a);
+}
+
+gint RrMinWidth(RrAppearance *a)
+{
+ gint i;
+ RrSize *m;
+ gint l, t, r, b;
+ gint w = 0;
+
+ RrMargins(a, &l, &t, &r, &b);
+
+ for (i = 0; i < a->textures; ++i) {
+ switch (a->texture[i].type) {
+ case RR_TEXTURE_NONE:
+ break;
+ case RR_TEXTURE_MASK:
+ w = MAX(w, a->texture[i].data.mask.mask->width);
+ break;
+ case RR_TEXTURE_TEXT:
+ m = RrFontMeasureString(a->texture[i].data.text.font,
+ a->texture[i].data.text.string,
+ a->texture[i].data.text.shadow_offset_x,
+ a->texture[i].data.text.shadow_offset_y,
+ a->texture[i].data.text.flow,
+ a->texture[i].data.text.maxwidth);
+ w = MAX(w, m->width);
+ g_slice_free(RrSize, m);
+ break;
+ case RR_TEXTURE_RGBA:
+ w += MAX(w, a->texture[i].data.rgba.width);
+ break;
+ case RR_TEXTURE_IMAGE:
+ /* images resize so they don't contribute anything to the min */
+ break;
+ case RR_TEXTURE_LINE_ART:
+ w = MAX(w, MAX(a->texture[i].data.lineart.x1 - l - r,
+ a->texture[i].data.lineart.x2 - l - r));
+ break;
+ case RR_TEXTURE_NUM_TYPES:
+ g_assert_not_reached();
+ }
+ }
+
+ w += l + r;
+
+ if (w < 1) w = 1;
+ return w;
+}
+
+gint RrMinHeight(RrAppearance *a)
+{
+ gint i;
+ gint l, t, r, b;
+ RrSize *m;
+ gint h = 0;
+
+ RrMargins(a, &l, &t, &r, &b);
+
+ for (i = 0; i < a->textures; ++i) {
+ switch (a->texture[i].type) {
+ case RR_TEXTURE_NONE:
+ break;
+ case RR_TEXTURE_MASK:
+ h = MAX(h, a->texture[i].data.mask.mask->height);
+ break;
+ case RR_TEXTURE_TEXT:
+ if (a->texture[i].data.text.flow) {
+ g_assert(a->texture[i].data.text.string != NULL);
+
+ m = RrFontMeasureString
+ (a->texture[i].data.text.font,
+ a->texture[i].data.text.string,
+ a->texture[i].data.text.shadow_offset_x,
+ a->texture[i].data.text.shadow_offset_y,
+ a->texture[i].data.text.flow,
+ a->texture[i].data.text.maxwidth);
+ h += MAX(h, m->height);
+ g_slice_free(RrSize, m);
+ }
+ else
+ h += MAX(h,
+ RrFontHeight
+ (a->texture[i].data.text.font,
+ a->texture[i].data.text.shadow_offset_y));
+ break;
+ case RR_TEXTURE_RGBA:
+ h += MAX(h, a->texture[i].data.rgba.height);
+ break;
+ case RR_TEXTURE_IMAGE:
+ /* images resize so they don't contribute anything to the min */
+ break;
+ case RR_TEXTURE_LINE_ART:
+ h = MAX(h, MAX(a->texture[i].data.lineart.y1 - t - b,
+ a->texture[i].data.lineart.y2 - t - b));
+ break;
+ case RR_TEXTURE_NUM_TYPES:
+ g_assert_not_reached();
+ }
+ }
+
+ h += t + b;
+
+ if (h < 1) h = 1;
+ return h;
+}
+
+static void reverse_bits(gchar *c, gint n)
+{
+ gint i;
+ for (i = 0; i < n; i++, c++)
+ *c = (((*c * 0x0802UL & 0x22110UL) |
+ (*c * 0x8020UL & 0x88440UL)) * 0x10101UL) >> 16;
+}
+
+gboolean RrPixmapToRGBA(const RrInstance *inst,
+ Pixmap pmap, Pixmap mask,
+ gint *w, gint *h, RrPixel32 **data)
+{
+ Window xr;
+ gint xx, xy;
+ guint pw, ph, mw, mh, xb, xd, i, x, y, di;
+ XImage *xi, *xm = NULL;
+
+ if (!XGetGeometry(RrDisplay(inst), pmap,
+ &xr, &xx, &xy, &pw, &ph, &xb, &xd))
+ return FALSE;
+
+ if (mask) {
+ if (!XGetGeometry(RrDisplay(inst), mask,
+ &xr, &xx, &xy, &mw, &mh, &xb, &xd))
+ return FALSE;
+ if (pw != mw || ph != mh || xd != 1)
+ return FALSE;
+ }
+
+ xi = XGetImage(RrDisplay(inst), pmap,
+ 0, 0, pw, ph, 0xffffffff, ZPixmap);
+ if (!xi)
+ return FALSE;
+
+ if (mask) {
+ xm = XGetImage(RrDisplay(inst), mask,
+ 0, 0, mw, mh, 0xffffffff, ZPixmap);
+ if (!xm) {
+ XDestroyImage(xi);
+ return FALSE;
+ }
+ if ((xm->bits_per_pixel == 1) && (xm->bitmap_bit_order != LSBFirst))
+ reverse_bits(xm->data, xm->bytes_per_line * xm->height);
+ }
+
+ if ((xi->bits_per_pixel == 1) && (xi->bitmap_bit_order != LSBFirst))
+ reverse_bits(xi->data, xi->bytes_per_line * xi->height);
+
+ *data = g_new(RrPixel32, pw * ph);
+ RrIncreaseDepth(inst, *data, xi);
+
+ if (mask) {
+ /* apply transparency from the mask */
+ di = 0;
+ for (i = 0, y = 0; y < ph; ++y) {
+ for (x = 0; x < pw; ++x, ++i) {
+ if (!((((unsigned)xm->data[di + x / 8]) >> (x % 8)) & 0x1))
+ (*data)[i] &= ~(0xff << RrDefaultAlphaOffset);
+ }
+ di += xm->bytes_per_line;
+ }
+ }
+
+ *w = pw;
+ *h = ph;
+
+ XDestroyImage(xi);
+ if (mask)
+ XDestroyImage(xm);
+
+ return TRUE;
+}
diff --git a/obrender/render.h b/obrender/render.h
new file mode 100644
index 0000000..a5d6500
--- /dev/null
+++ b/obrender/render.h
@@ -0,0 +1,462 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ render.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __render_h
+#define __render_h
+
+#include <X11/Xlib.h> /* some platforms dont include this as needed for Xft */
+#include <pango/pangoxft.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#include "obrender/geom.h"
+#include "obrender/version.h"
+
+typedef union _RrTextureData RrTextureData;
+typedef struct _RrAppearance RrAppearance;
+typedef struct _RrSurface RrSurface;
+typedef struct _RrFont RrFont;
+typedef struct _RrTexture RrTexture;
+typedef struct _RrTextureMask RrTextureMask;
+typedef struct _RrTextureRGBA RrTextureRGBA;
+typedef struct _RrTextureImage RrTextureImage;
+typedef struct _RrTextureText RrTextureText;
+typedef struct _RrTextureLineArt RrTextureLineArt;
+typedef struct _RrPixmapMask RrPixmapMask;
+typedef struct _RrInstance RrInstance;
+typedef struct _RrColor RrColor;
+typedef struct _RrImage RrImage;
+typedef struct _RrImageSet RrImageSet;
+typedef struct _RrImagePic RrImagePic;
+typedef struct _RrImageCache RrImageCache;
+typedef struct _RrButton RrButton;
+
+typedef guint32 RrPixel32; /* RGBA format */
+typedef guint16 RrPixel16;
+typedef guchar RrPixel8;
+
+typedef enum {
+ RR_RELIEF_FLAT,
+ RR_RELIEF_RAISED,
+ RR_RELIEF_SUNKEN,
+ RR_RELIEF_NUM_TYPES
+} RrReliefType;
+
+typedef enum {
+ RR_BEVEL_1,
+ RR_BEVEL_2,
+ RR_BEVEL_NUM_TYPES
+} RrBevelType;
+
+typedef enum {
+ RR_SURFACE_NONE,
+ RR_SURFACE_PARENTREL,
+ RR_SURFACE_SOLID,
+ RR_SURFACE_SPLIT_VERTICAL,
+ RR_SURFACE_HORIZONTAL,
+ RR_SURFACE_VERTICAL,
+ RR_SURFACE_DIAGONAL,
+ RR_SURFACE_CROSS_DIAGONAL,
+ RR_SURFACE_PYRAMID,
+ RR_SURFACE_MIRROR_HORIZONTAL,
+ RR_SURFACE_NUM_TYPES
+} RrSurfaceColorType;
+
+typedef enum {
+ RR_TEXTURE_NONE,
+ RR_TEXTURE_MASK,
+ RR_TEXTURE_TEXT,
+ RR_TEXTURE_LINE_ART,
+ RR_TEXTURE_RGBA,
+ RR_TEXTURE_IMAGE,
+ RR_TEXTURE_NUM_TYPES
+} RrTextureType;
+
+typedef enum {
+ RR_JUSTIFY_LEFT,
+ RR_JUSTIFY_CENTER,
+ RR_JUSTIFY_RIGHT,
+ RR_JUSTIFY_NUM_TYPES
+} RrJustify;
+
+/* Put middle first so it's the default */
+typedef enum {
+ RR_ELLIPSIZE_MIDDLE,
+ RR_ELLIPSIZE_NONE,
+ RR_ELLIPSIZE_START,
+ RR_ELLIPSIZE_END,
+ RR_ELLIPSIZE_NUM_TYPES
+} RrEllipsizeMode;
+
+typedef enum {
+ RR_FONTWEIGHT_LIGHT,
+ RR_FONTWEIGHT_NORMAL,
+ RR_FONTWEIGHT_SEMIBOLD,
+ RR_FONTWEIGHT_BOLD,
+ RR_FONTWEIGHT_ULTRABOLD,
+ RR_FONTWEIGHT_NUM_TYPES
+} RrFontWeight;
+
+typedef enum {
+ RR_FONTSLANT_NORMAL,
+ RR_FONTSLANT_ITALIC,
+ RR_FONTSLANT_OBLIQUE,
+ RR_FONTSLANT_NUM_TYPES
+} RrFontSlant;
+
+struct _RrSurface {
+ RrSurfaceColorType grad;
+ RrReliefType relief;
+ RrBevelType bevel;
+ RrColor *primary;
+ RrColor *secondary;
+ RrColor *border_color;
+ RrColor *bevel_dark;
+ RrColor *bevel_light;
+ RrColor *interlace_color;
+ gboolean interlaced;
+ gboolean border;
+ RrAppearance *parent;
+ gint parentx;
+ gint parenty;
+ RrPixel32 *pixel_data;
+ gint bevel_dark_adjust; /* 0-255, default is 64 */
+ gint bevel_light_adjust; /* 0-255, default is 128 */
+ RrColor *split_primary;
+ RrColor *split_secondary;
+};
+
+struct _RrTextureText {
+ RrFont *font;
+ RrJustify justify;
+ RrColor *color;
+ const gchar *string;
+ gint shadow_offset_x;
+ gint shadow_offset_y;
+ RrColor *shadow_color;
+ gboolean shortcut; /*!< Underline a character */
+ guint shortcut_pos; /*!< Position in bytes of the character to underline */
+ RrEllipsizeMode ellipsize;
+ gboolean flow; /* allow multiple lines. must set maxwidth below */
+ gint maxwidth;
+ guchar shadow_alpha; /* at the bottom to improve alignment */
+};
+
+struct _RrPixmapMask {
+ const RrInstance *inst;
+ Pixmap mask;
+ gint width;
+ gint height;
+ gchar *data;
+};
+
+struct _RrTextureMask {
+ RrColor *color;
+ RrPixmapMask *mask;
+};
+
+struct _RrTextureRGBA {
+ gint width;
+ gint height;
+ gint alpha;
+ RrPixel32 *data;
+ /* size and position to draw at (if these are zero, then it will be
+ drawn to fill the entire texture */
+ gint tx;
+ gint ty;
+ gint twidth;
+ gint theight;
+};
+
+struct _RrTextureImage {
+ RrImage *image;
+ gint alpha;
+ /* size and position to draw at (if these are zero, then it will be
+ drawn to fill the entire texture */
+ gint tx;
+ gint ty;
+ gint twidth;
+ gint theight;
+};
+
+struct _RrTextureLineArt {
+ RrColor *color;
+ gint x1;
+ gint y1;
+ gint x2;
+ gint y2;
+};
+
+union _RrTextureData {
+ RrTextureRGBA rgba;
+ RrTextureImage image;
+ RrTextureText text;
+ RrTextureMask mask;
+ RrTextureLineArt lineart;
+};
+
+struct _RrTexture {
+ /* If changing the type of a texture, you should DEFINITELY call
+ RrAppearanceClearTextures() first! */
+ RrTextureType type;
+ RrTextureData data;
+};
+
+struct _RrAppearance {
+ const RrInstance *inst;
+
+ RrSurface surface;
+ gint textures;
+ RrTexture *texture;
+ Pixmap pixmap;
+ XftDraw *xftdraw;
+
+ /* cached for internal use */
+ gint w, h;
+};
+
+/*! Holds a RGBA image picture */
+struct _RrImagePic {
+ gint width, height;
+ RrPixel32 *data;
+ /* The sum of all the pixels. This is used to compare pictures if their
+ hashes match. */
+ gint sum;
+};
+
+typedef void (*RrImageDestroyFunc)(RrImage *image, gpointer data);
+
+/*! An RrImage refers to a RrImageSet. If multiple RrImageSets end up
+ holding the same image data, they will be marged and the RrImages that
+ point to them would be updated. */
+struct _RrImage {
+ gint ref;
+ RrImageSet *set;
+
+ /* This function (if not NULL) will be called just before destroying
+ RrImage. */
+ RrImageDestroyFunc destroy_func;
+ gpointer destroy_data;
+};
+
+/*! An RrImage is a sort of meta-image. It can contain multiple versions
+ of an image at different sizes, which may or may not be completely different
+ pictures */
+struct _RrImageSet
+{
+ RrImageCache *cache;
+
+ /*! If a picture is loaded by a name, then it has a name attached to it.
+ This contains a list of strings, containing all names that have ever
+ been associated with the RrImageSet. A name in the RrImageCache can
+ only be associated with a single RrImageSet. */
+ GSList *names;
+
+ /*! RrImages that point at this RrImageSet. If this is empty, then there
+ are no images using the set and it can be freed. */
+ GSList *images;
+
+ /*! An array of "originals", that is of RrPictures that have been added
+ to the image in various sizes, and that have not been resized. These
+ are explicitly added to the RrImageSet. */
+ RrImagePic **original;
+ gint n_original;
+ /*! An array of "resized" pictures. When an "original" RrPicture
+ needs to be resized for drawing, it is saved in here so that it doesn't
+ need to be resized again. These are automatically added to the
+ RrImage. */
+ RrImagePic **resized;
+ gint n_resized;
+};
+
+struct _RrButton {
+ const RrInstance *inst;
+
+ /* colors */
+ RrColor *focused_unpressed_color;
+ RrColor *unfocused_unpressed_color;
+ RrColor *focused_pressed_color;
+ RrColor *unfocused_pressed_color;
+ RrColor *disabled_focused_color;
+ RrColor *disabled_unfocused_color;
+ RrColor *hover_focused_color;
+ RrColor *hover_unfocused_color;
+ RrColor *toggled_hover_focused_color;
+ RrColor *toggled_hover_unfocused_color;
+ RrColor *toggled_focused_pressed_color;
+ RrColor *toggled_unfocused_pressed_color;
+ RrColor *toggled_focused_unpressed_color;
+ RrColor *toggled_unfocused_unpressed_color;
+
+ /* masks */
+ RrPixmapMask *mask;
+ RrPixmapMask *pressed_mask;
+ RrPixmapMask *disabled_mask;
+ RrPixmapMask *hover_mask;
+ RrPixmapMask *toggled_mask;
+ RrPixmapMask *toggled_hover_mask;
+ RrPixmapMask *toggled_pressed_mask;
+
+ /* textures */
+ RrAppearance *a_focused_unpressed;
+ RrAppearance *a_unfocused_unpressed;
+ RrAppearance *a_focused_pressed;
+ RrAppearance *a_unfocused_pressed;
+ RrAppearance *a_disabled_focused;
+ RrAppearance *a_disabled_unfocused;
+ RrAppearance *a_hover_focused;
+ RrAppearance *a_hover_unfocused;
+ RrAppearance *a_toggled_focused_unpressed;
+ RrAppearance *a_toggled_unfocused_unpressed;
+ RrAppearance *a_toggled_focused_pressed;
+ RrAppearance *a_toggled_unfocused_pressed;
+ RrAppearance *a_toggled_hover_focused;
+ RrAppearance *a_toggled_hover_unfocused;
+
+};
+
+/* these are the same on all endian machines because it seems to be dependant
+ on the endianness of the gfx card, not the cpu. */
+#define RrDefaultAlphaOffset 24
+#define RrDefaultRedOffset 16
+#define RrDefaultGreenOffset 8
+#define RrDefaultBlueOffset 0
+
+#define RrDefaultFontFamily "arial,sans"
+#define RrDefaultFontSize 8
+#define RrDefaultFontWeight RR_FONTWEIGHT_NORMAL
+#define RrDefaultFontSlant RR_FONTSLANT_NORMAL
+
+RrInstance* RrInstanceNew (Display *display, gint screen);
+void RrInstanceFree (RrInstance *inst);
+
+Display* RrDisplay (const RrInstance *inst);
+gint RrScreen (const RrInstance *inst);
+Window RrRootWindow (const RrInstance *inst);
+Visual* RrVisual (const RrInstance *inst);
+gint RrDepth (const RrInstance *inst);
+Colormap RrColormap (const RrInstance *inst);
+gint RrRedOffset (const RrInstance *inst);
+gint RrGreenOffset (const RrInstance *inst);
+gint RrBlueOffset (const RrInstance *inst);
+gint RrRedShift (const RrInstance *inst);
+gint RrGreenShift (const RrInstance *inst);
+gint RrBlueShift (const RrInstance *inst);
+gint RrRedMask (const RrInstance *inst);
+gint RrGreenMask (const RrInstance *inst);
+gint RrBlueMask (const RrInstance *inst);
+
+RrColor *RrColorNew (const RrInstance *inst, gint r, gint g, gint b);
+RrColor *RrColorCopy (RrColor *c);
+RrColor *RrColorParse (const RrInstance *inst, gchar *colorname);
+void RrColorFree (RrColor *in);
+
+gint RrColorRed (const RrColor *c);
+gint RrColorGreen (const RrColor *c);
+gint RrColorBlue (const RrColor *c);
+gulong RrColorPixel (const RrColor *c);
+GC RrColorGC (RrColor *c);
+
+RrAppearance *RrAppearanceNew (const RrInstance *inst, gint numtex);
+RrAppearance *RrAppearanceCopy (RrAppearance *a);
+void RrAppearanceFree (RrAppearance *a);
+void RrAppearanceRemoveTextures(RrAppearance *a);
+void RrAppearanceAddTextures(RrAppearance *a, gint numtex);
+/*! Always call this when changing the type of a texture in an appearance */
+void RrAppearanceClearTextures(RrAppearance *a);
+
+RrButton *RrButtonNew (const RrInstance *inst);
+void RrButtonFree(RrButton *b);
+
+RrFont *RrFontOpen (const RrInstance *inst, const gchar *name,
+ gint size, RrFontWeight weight, RrFontSlant slant);
+RrFont *RrFontOpenDefault (const RrInstance *inst);
+void RrFontClose (RrFont *f);
+/*! Returns an RrSize, that was allocated with g_slice_new(). Use g_slice_free() to
+ free it. */
+RrSize *RrFontMeasureString (const RrFont *f, const gchar *str,
+ gint shadow_offset_x, gint shadow_offset_y,
+ gboolean flow, gint maxwidth);
+gint RrFontHeight (const RrFont *f, gint shadow_offset_y);
+gint RrFontMaxCharWidth (const RrFont *f);
+
+/* Paint into the appearance. The old pixmap is returned (if there was one). It
+ is the responsibility of the caller to call XFreePixmap on the return when
+ it is non-null. */
+Pixmap RrPaintPixmap (RrAppearance *a, gint w, gint h);
+void RrPaint (RrAppearance *a, Window win, gint w, gint h);
+void RrMinSize (RrAppearance *a, gint *w, gint *h);
+gint RrMinWidth (RrAppearance *a);
+/* For text textures, if flow is TRUE, then the string must be set before
+ calling this, otherwise it doesn't need to be */
+gint RrMinHeight (RrAppearance *a);
+void RrMargins (RrAppearance *a, gint *l, gint *t, gint *r, gint *b);
+
+gboolean RrPixmapToRGBA(const RrInstance *inst,
+ Pixmap pmap, Pixmap mask,
+ gint *w, gint *h, RrPixel32 **data);
+
+/*! Create a new image cache for RrImages.
+ @param max_resized_saved The number of resized copies of an image to save
+*/
+RrImageCache* RrImageCacheNew(gint max_resized_saved);
+void RrImageCacheRef(RrImageCache *self);
+void RrImageCacheUnref(RrImageCache *self);
+
+/*! Create a new image, or return one from the cache that matches.
+ @param cache The image cache.
+ @param old The current RrImage, which the new image should be added to.
+ Use this if loading a different sized version of the same image.
+ The returned RrImage should replace the one passed in as old.
+ Pass NULL here if adding an image which is (or may be) entirely new.
+ @param name The name of the icon to be loaded off disk, or used in the cache
+ @return Returns NULL if unable to load an image by the name and it is not in
+ the cache already
+*/
+RrImage* RrImageNewFromName(RrImageCache *cache, const gchar *name);
+
+/*! Create a new image, or return one from the cache that matches.
+ @param cache The image cache.
+ @param data The image data in RGBA32 format. There should be @w * @h many
+ values in the data array.
+ @param w The width of the image data.
+ @param h The height of the image data.
+ @return Returns NULL if unable to load an image by the name and it is not in
+ the cache already
+*/
+RrImage* RrImageNewFromData(RrImageCache *cache, RrPixel32 *data,
+ gint w, gint h);
+
+/*! Add a new size of a picture to an image.
+ If a picture has multiple versions of different sizes (example 16x16, 32x32
+ and so on), they should all be under the same RrImage. This adds a new
+ size to an existing RrImage, associating the newly sized picture with the
+ others in the RrImage - classifying them as being the same logical image at a
+ different dimention.
+*/
+void RrImageAddFromData(RrImage *image, RrPixel32 *data, gint w, gint h);
+
+void RrImageRef(RrImage *im);
+void RrImageUnref(RrImage *im);
+
+G_END_DECLS
+
+#endif /*__render_h*/
diff --git a/obrender/test.c b/obrender/test.c
new file mode 100644
index 0000000..55ab621
--- /dev/null
+++ b/obrender/test.c
@@ -0,0 +1,115 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ test.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ 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 <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/shape.h>
+#include <string.h>
+#include <stdlib.h>
+#include "render.h"
+#include <glib.h>
+
+static gint x_error_handler(Display * disp, XErrorEvent * error)
+{
+ gchar buf[1024];
+ XGetErrorText(disp, error->error_code, buf, 1024);
+ printf("%s\n", buf);
+ return 0;
+}
+
+Display *ob_display;
+gint ob_screen;
+Window ob_root;
+
+gint main()
+{
+ Window win;
+ RrInstance *inst;
+ RrAppearance *look;
+ int done;
+
+ Window root;
+ XEvent report;
+ gint h = 500, w = 500;
+
+ ob_display = XOpenDisplay(NULL);
+ XSetErrorHandler(x_error_handler);
+ ob_screen = DefaultScreen(ob_display);
+ ob_root = RootWindow(ob_display, ob_screen);
+ win =
+ XCreateWindow(ob_display, RootWindow(ob_display, 0),
+ 10, 10, w, h, 10,
+ CopyFromParent, /* depth */
+ CopyFromParent, /* class */
+ CopyFromParent, /* visual */
+ 0, /* valuemask */
+ 0); /* attributes */
+ XMapWindow(ob_display, win);
+ XSelectInput(ob_display, win, ExposureMask | StructureNotifyMask);
+ root = RootWindow (ob_display, DefaultScreen (ob_display));
+ inst = RrInstanceNew(ob_display, ob_screen);
+
+ look = RrAppearanceNew(inst, 0);
+ look->surface.grad = RR_SURFACE_MIRROR_HORIZONTAL;
+ look->surface.secondary = RrColorParse(inst, "Yellow");
+ look->surface.split_secondary = RrColorParse(inst, "Red");
+ look->surface.split_primary = RrColorParse(inst, "Green");
+ look->surface.primary = RrColorParse(inst, "Blue");
+ look->surface.interlaced = FALSE;
+ if (ob_display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+#if BIGTEST
+ int i;
+ look->surface.pixel_data = g_new(RrPixel32, w*h);
+ for (i = 0; i < 10000; ++i) {
+ printf("\r%d", i);
+ fflush(stdout);
+ RrRender(look, w, h);
+ }
+ exit (0);
+#endif
+
+ RrPaint(look, win, w, h);
+ done = 0;
+ while (!done) {
+ XNextEvent(ob_display, &report);
+ switch (report.type) {
+ case Expose:
+ break;
+ case ConfigureNotify:
+ RrPaint(look, win,
+ report.xconfigure.width,
+ report.xconfigure.height);
+ break;
+ case UnmapNotify:
+ done = 1;
+ break;
+ }
+ }
+
+ RrAppearanceFree (look);
+ RrInstanceFree (inst);
+
+ return 1;
+}
diff --git a/obrender/theme.c b/obrender/theme.c
new file mode 100644
index 0000000..b20182a
--- /dev/null
+++ b/obrender/theme.c
@@ -0,0 +1,2169 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ theme.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "render.h"
+#include "color.h"
+#include "font.h"
+#include "mask.h"
+#include "theme.h"
+#include "icon.h"
+#include "obt/paths.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+static XrmDatabase loaddb(const gchar *name, gchar **path);
+static gboolean read_int(XrmDatabase db, const gchar *rname, gint *value);
+static gboolean read_string(XrmDatabase db, const gchar *rname, gchar **value);
+static gboolean read_color(XrmDatabase db, const RrInstance *inst,
+ const gchar *rname, RrColor **value);
+static gboolean read_mask(const RrInstance *inst, const gchar *path,
+ RrTheme *theme, const gchar *maskname,
+ RrPixmapMask **value);
+static gboolean read_appearance(XrmDatabase db, const RrInstance *inst,
+ const gchar *rname, RrAppearance *value,
+ gboolean allow_trans);
+static int parse_inline_number(const char *p);
+static RrPixel32* read_c_image(gint width, gint height, const guint8 *data);
+static void set_default_appearance(RrAppearance *a);
+static void read_button_colors(XrmDatabase db, const RrInstance *inst,
+ const RrTheme *theme, RrButton *btn,
+ const gchar *btnname);
+
+static RrFont *get_font(RrFont *target, RrFont **default_font,
+ const RrInstance *inst)
+{
+ if (target) {
+ RrFontRef(target);
+ return target;
+ } else {
+ /* Only load the default font once */
+ if (*default_font) {
+ RrFontRef(*default_font);
+ } else {
+ *default_font = RrFontOpenDefault(inst);
+ }
+ return *default_font;
+ }
+}
+
+#define READ_INT(x_resstr, x_var, x_min, x_max, x_def) \
+ if (!read_int(db, x_resstr, & x_var) || \
+ x_var < x_min || x_var > x_max) \
+ x_var = x_def;
+
+#define READ_COLOR(x_resstr, x_var, x_def) \
+ if (!read_color(db, inst, x_resstr, & x_var)) \
+ x_var = x_def;
+
+#define READ_COLOR_(x_res1, x_res2, x_var, x_def) \
+ if (!read_color(db, inst, x_res1, & x_var) && \
+ !read_color(db, inst, x_res2, & x_var)) \
+ x_var = x_def;
+
+#define READ_MASK_COPY(x_file, x_var, x_copysrc) \
+ if (!read_mask(inst, path, theme, x_file, & x_var)) \
+ x_var = RrPixmapMaskCopy(x_copysrc);
+
+#define READ_APPEARANCE(x_resstr, x_var, x_parrel) \
+ if (!read_appearance(db, inst, x_resstr, x_var, x_parrel)) \
+ set_default_appearance(x_var);
+
+#define READ_APPEARANCE_COPY(x_resstr, x_var, x_parrel, x_defval) \
+ if (!read_appearance(db, inst, x_resstr, x_var, x_parrel)) {\
+ RrAppearanceFree(x_var); \
+ x_var = RrAppearanceCopy(x_defval); }
+
+#define READ_APPEARANCE_COPY_TEXTURES(x_resstr, x_var, x_parrel, x_defval, n_tex) \
+ if (!read_appearance(db, inst, x_resstr, x_var, x_parrel)) {\
+ RrAppearanceFree(x_var); \
+ x_var = RrAppearanceCopy(x_defval); \
+ RrAppearanceRemoveTextures(x_var); \
+ RrAppearanceAddTextures(x_var, 5); }
+
+#define READ_APPEARANCE_(x_res1, x_res2, x_var, x_parrel, x_defval) \
+ if (!read_appearance(db, inst, x_res1, x_var, x_parrel) && \
+ !read_appearance(db, inst, x_res2, x_var, x_parrel)) {\
+ RrAppearanceFree(x_var); \
+ x_var = RrAppearanceCopy(x_defval); }
+
+RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name,
+ gboolean allow_fallback,
+ RrFont *active_window_font, RrFont *inactive_window_font,
+ RrFont *menu_title_font, RrFont *menu_item_font,
+ RrFont *active_osd_font, RrFont *inactive_osd_font)
+{
+ XrmDatabase db = NULL;
+ RrJustify winjust, mtitlejust;
+ gchar *str;
+ RrTheme *theme;
+ RrFont *default_font = NULL;
+ gchar *path;
+ gboolean userdef;
+ gint menu_overlap = 0;
+ RrAppearance *a_disabled_focused_tmp;
+ RrAppearance *a_disabled_unfocused_tmp;
+ RrAppearance *a_hover_focused_tmp;
+ RrAppearance *a_hover_unfocused_tmp;
+ RrAppearance *a_focused_unpressed_tmp;
+ RrAppearance *a_focused_pressed_tmp;
+ RrAppearance *a_unfocused_unpressed_tmp;
+ RrAppearance *a_unfocused_pressed_tmp;
+ RrAppearance *a_toggled_hover_focused_tmp;
+ RrAppearance *a_toggled_hover_unfocused_tmp;
+ RrAppearance *a_toggled_focused_unpressed_tmp;
+ RrAppearance *a_toggled_focused_pressed_tmp;
+ RrAppearance *a_toggled_unfocused_unpressed_tmp;
+ RrAppearance *a_toggled_unfocused_pressed_tmp;
+
+ if (name) {
+ db = loaddb(name, &path);
+ if (db == NULL) {
+ g_message("Unable to load the theme '%s'", name);
+ if (allow_fallback)
+ g_message("Falling back to the default theme '%s'",
+ DEFAULT_THEME);
+ /* fallback to the default theme */
+ name = NULL;
+ }
+ }
+ if (name == NULL) {
+ if (allow_fallback) {
+ db = loaddb(DEFAULT_THEME, &path);
+ if (db == NULL) {
+ g_message("Unable to load the theme '%s'", DEFAULT_THEME);
+ return NULL;
+ }
+ } else
+ return NULL;
+ }
+
+ /* initialize temp reading textures */
+ a_disabled_focused_tmp = RrAppearanceNew(inst, 1);
+ a_disabled_unfocused_tmp = RrAppearanceNew(inst, 1);
+ a_hover_focused_tmp = RrAppearanceNew(inst, 1);
+ a_hover_unfocused_tmp = RrAppearanceNew(inst, 1);
+ a_toggled_focused_unpressed_tmp = RrAppearanceNew(inst, 1);
+ a_toggled_unfocused_unpressed_tmp = RrAppearanceNew(inst, 1);
+ a_toggled_hover_focused_tmp = RrAppearanceNew(inst, 1);
+ a_toggled_hover_unfocused_tmp = RrAppearanceNew(inst, 1);
+ a_toggled_focused_pressed_tmp = RrAppearanceNew(inst, 1);
+ a_toggled_unfocused_pressed_tmp = RrAppearanceNew(inst, 1);
+ a_focused_unpressed_tmp = RrAppearanceNew(inst, 1);
+ a_focused_pressed_tmp = RrAppearanceNew(inst, 1);
+ a_unfocused_unpressed_tmp = RrAppearanceNew(inst, 1);
+ a_unfocused_pressed_tmp = RrAppearanceNew(inst, 1);
+
+ /* initialize theme */
+ theme = g_slice_new0(RrTheme);
+
+ theme->inst = inst;
+ theme->name = g_strdup(name ? name : DEFAULT_THEME);
+
+ /* init buttons */
+ theme->btn_max = RrButtonNew(inst);
+ theme->btn_close = RrButtonNew(inst);
+ theme->btn_desk = RrButtonNew(inst);
+ theme->btn_shade = RrButtonNew(inst);
+ theme->btn_iconify = RrButtonNew(inst);
+
+ /* init appearances */
+ theme->a_focused_grip = RrAppearanceNew(inst, 0);
+ theme->a_unfocused_grip = RrAppearanceNew(inst, 0);
+ theme->a_focused_title = RrAppearanceNew(inst, 0);
+ theme->a_unfocused_title = RrAppearanceNew(inst, 0);
+ theme->a_focused_label = RrAppearanceNew(inst, 1);
+ theme->a_unfocused_label = RrAppearanceNew(inst, 1);
+ theme->a_icon = RrAppearanceNew(inst, 1);
+ theme->a_focused_handle = RrAppearanceNew(inst, 0);
+ theme->a_unfocused_handle = RrAppearanceNew(inst, 0);
+ theme->a_menu = RrAppearanceNew(inst, 0);
+ theme->a_menu_title = RrAppearanceNew(inst, 0);
+ theme->a_menu_text_title = RrAppearanceNew(inst, 1);
+ theme->a_menu_normal = RrAppearanceNew(inst, 0);
+ theme->a_menu_selected = RrAppearanceNew(inst, 0);
+ theme->a_menu_disabled = RrAppearanceNew(inst, 0);
+ /* a_menu_disabled_selected is copied from a_menu_selected */
+ theme->a_menu_text_normal = RrAppearanceNew(inst, 1);
+ theme->a_menu_text_selected = RrAppearanceNew(inst, 1);
+ theme->a_menu_text_disabled = RrAppearanceNew(inst, 1);
+ theme->a_menu_text_disabled_selected = RrAppearanceNew(inst, 1);
+ theme->a_menu_bullet_normal = RrAppearanceNew(inst, 1);
+ theme->a_menu_bullet_selected = RrAppearanceNew(inst, 1);
+ theme->a_clear = RrAppearanceNew(inst, 0);
+ theme->a_clear_tex = RrAppearanceNew(inst, 1);
+ theme->osd_bg = RrAppearanceNew(inst, 0);
+ theme->osd_hilite_label = RrAppearanceNew(inst, 1);
+ theme->osd_hilite_bg = RrAppearanceNew(inst, 0);
+ theme->osd_unhilite_label = RrAppearanceNew(inst, 1);
+ theme->osd_unhilite_bg = RrAppearanceNew(inst, 0);
+ theme->osd_unpressed_button = RrAppearanceNew(inst, 1);
+ theme->osd_pressed_button = RrAppearanceNew(inst, 5);
+ theme->osd_focused_button = RrAppearanceNew(inst, 5);
+
+ /* load the font stuff */
+ theme->win_font_focused = get_font(active_window_font,
+ &default_font, inst);
+ theme->win_font_unfocused = get_font(inactive_window_font,
+ &default_font, inst);
+
+ winjust = RR_JUSTIFY_LEFT;
+ if (read_string(db, "window.label.text.justify", &str)) {
+ if (!g_ascii_strcasecmp(str, "right"))
+ winjust = RR_JUSTIFY_RIGHT;
+ else if (!g_ascii_strcasecmp(str, "center"))
+ winjust = RR_JUSTIFY_CENTER;
+ }
+
+ theme->menu_title_font = get_font(menu_title_font, &default_font, inst);
+
+ mtitlejust = RR_JUSTIFY_LEFT;
+ if (read_string(db, "menu.title.text.justify", &str)) {
+ if (!g_ascii_strcasecmp(str, "right"))
+ mtitlejust = RR_JUSTIFY_RIGHT;
+ else if (!g_ascii_strcasecmp(str, "center"))
+ mtitlejust = RR_JUSTIFY_CENTER;
+ }
+
+ theme->menu_font = get_font(menu_item_font, &default_font, inst);
+
+ theme->osd_font_hilite = get_font(active_osd_font, &default_font, inst);
+ theme->osd_font_unhilite = get_font(inactive_osd_font, &default_font,inst);
+
+ /* load direct dimensions */
+ READ_INT("menu.overlap", menu_overlap, -100, 100, 0);
+ READ_INT("menu.overlap.x", theme->menu_overlap_x, -100, 100, menu_overlap);
+ READ_INT("menu.overlap.y", theme->menu_overlap_y, -100, 100, menu_overlap);
+ READ_INT("window.handle.width", theme->handle_height, 0, 100, 6);
+ READ_INT("padding.width", theme->paddingx, 0, 100, 3);
+ READ_INT("padding.height", theme->paddingy, 0, 100, theme->paddingx);
+ READ_INT("border.width", theme->fbwidth, 0, 100, 1);
+ READ_INT("menu.border.width", theme->mbwidth, 0, 100, theme->fbwidth);
+ READ_INT("osd.border.width", theme->obwidth, 0, 100, theme->fbwidth);
+ READ_INT("undecorated.border.width", theme->ubwidth, 0, 100,
+ theme->fbwidth);
+ READ_INT("menu.separator.width", theme->menu_sep_width, 1, 100, 1);
+ READ_INT("menu.separator.padding.width", theme->menu_sep_paddingx,
+ 0, 100, 6);
+ READ_INT("menu.separator.padding.height", theme->menu_sep_paddingy,
+ 0, 100, 3);
+ READ_INT("window.client.padding.width", theme->cbwidthx, 0, 100,
+ theme->paddingx);
+ READ_INT("window.client.padding.height", theme->cbwidthy, 0, 100,
+ theme->cbwidthx);
+
+ /* load colors */
+ READ_COLOR_("window.active.border.color", "border.color",
+ theme->frame_focused_border_color, RrColorNew(inst, 0, 0, 0));
+ /* undecorated focused border color inherits from frame focused border
+ color */
+ READ_COLOR("window.undecorated.active.border.color",
+ theme->frame_undecorated_focused_border_color,
+ RrColorCopy(theme->frame_focused_border_color));
+ /* title separator focused color inherits from focused border color */
+ READ_COLOR("window.active.title.separator.color",
+ theme->title_separator_focused_color,
+ RrColorCopy(theme->frame_focused_border_color));
+
+ /* unfocused border color inherits from frame focused border color */
+ READ_COLOR("window.inactive.border.color",
+ theme->frame_unfocused_border_color,
+ RrColorCopy(theme->frame_focused_border_color));
+
+ /* undecorated unfocused border color inherits from frame unfocused border
+ color */
+ READ_COLOR("window.undecorated.inactive.border.color",
+ theme->frame_undecorated_unfocused_border_color,
+ RrColorCopy(theme->frame_unfocused_border_color));
+
+ /* title separator unfocused color inherits from unfocused border color */
+ READ_COLOR("window.inactive.title.separator.color",
+ theme->title_separator_unfocused_color,
+ RrColorCopy(theme->frame_unfocused_border_color));
+
+ /* menu border color inherits from frame focused border color */
+ READ_COLOR("menu.border.color", theme->menu_border_color,
+ RrColorCopy(theme->frame_focused_border_color));
+
+ /* osd border color inherits from frame focused border color */
+ READ_COLOR("osd.border.color", theme->osd_border_color,
+ RrColorCopy(theme->frame_focused_border_color));
+
+ READ_COLOR("window.active.client.color", theme->cb_focused_color,
+ RrColorNew(inst, 0xff, 0xff, 0xff));
+
+ READ_COLOR("window.inactive.client.color", theme->cb_unfocused_color,
+ RrColorNew(inst, 0xff, 0xff, 0xff));
+
+ READ_COLOR("window.active.label.text.color", theme->title_focused_color,
+ RrColorNew(inst, 0x0, 0x0, 0x0));
+
+ READ_COLOR("window.inactive.label.text.color", theme->title_unfocused_color,
+ RrColorCopy(theme->title_unfocused_color));
+
+ READ_COLOR_("osd.active.label.text.color",
+ "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("window.active.button.unpressed.image.color",
+ theme->titlebut_focused_unpressed_color,
+ RrColorNew(inst, 0, 0, 0));
+
+ READ_COLOR("window.inactive.button.unpressed.image.color",
+ theme->titlebut_unfocused_unpressed_color,
+ RrColorNew(inst, 0xff, 0xff, 0xff));
+
+ READ_COLOR("window.active.button.pressed.image.color",
+ theme->titlebut_focused_pressed_color,
+ RrColorCopy(theme->titlebut_focused_unpressed_color));
+
+ READ_COLOR("window.inactive.button.pressed.image.color",
+ theme->titlebut_unfocused_pressed_color,
+ RrColorCopy(theme->titlebut_unfocused_unpressed_color));
+
+ READ_COLOR("window.active.button.disabled.image.color",
+ theme->titlebut_disabled_focused_color,
+ RrColorNew(inst, 0xff, 0xff, 0xff));
+
+ READ_COLOR("window.inactive.button.disabled.image.color",
+ theme->titlebut_disabled_unfocused_color,
+ RrColorNew(inst, 0, 0, 0));
+
+ READ_COLOR("window.active.button.hover.image.color",
+ theme->titlebut_hover_focused_color,
+ RrColorCopy(theme->titlebut_focused_unpressed_color));
+
+ READ_COLOR("window.inactive.button.hover.image.color",
+ theme->titlebut_hover_unfocused_color,
+ RrColorCopy(theme->titlebut_unfocused_unpressed_color));
+
+ READ_COLOR_("window.active.button.toggled.unpressed.image.color",
+ "window.active.button.toggled.image.color",
+ theme->titlebut_toggled_focused_unpressed_color,
+ RrColorCopy(theme->titlebut_focused_pressed_color));
+
+ READ_COLOR_("window.inactive.button.toggled.unpressed.image.color",
+ "window.inactive.button.toggled.image.color",
+ theme->titlebut_toggled_unfocused_unpressed_color,
+ RrColorCopy(theme->titlebut_unfocused_pressed_color));
+
+ READ_COLOR("window.active.button.toggled.hover.image.color",
+ theme->titlebut_toggled_hover_focused_color,
+ RrColorCopy(theme->titlebut_toggled_focused_unpressed_color));
+
+ READ_COLOR("window.inactive.button.toggled.hover.image.color",
+ theme->titlebut_toggled_hover_unfocused_color,
+ RrColorCopy(theme->titlebut_toggled_unfocused_unpressed_color));
+
+ READ_COLOR("window.active.button.toggled.pressed.image.color",
+ theme->titlebut_toggled_focused_pressed_color,
+ RrColorCopy(theme->titlebut_focused_pressed_color));
+
+ READ_COLOR("window.inactive.button.toggled.pressed.image.color",
+ theme->titlebut_toggled_unfocused_pressed_color,
+ RrColorCopy(theme->titlebut_unfocused_pressed_color));
+
+ READ_COLOR("menu.title.text.color", theme->menu_title_color,
+ RrColorNew(inst, 0, 0, 0));
+
+ READ_COLOR("menu.items.text.color", theme->menu_color,
+ RrColorNew(inst, 0xff, 0xff, 0xff));
+
+ READ_COLOR("menu.bullet.image.color", theme->menu_bullet_color,
+ RrColorCopy(theme->menu_color));
+
+ READ_COLOR("menu.items.disabled.text.color", theme->menu_disabled_color,
+ RrColorNew(inst, 0, 0, 0));
+
+ READ_COLOR("menu.items.active.disabled.text.color",
+ theme->menu_disabled_selected_color,
+ RrColorCopy(theme->menu_disabled_color));
+
+ READ_COLOR("menu.items.active.text.color", theme->menu_selected_color,
+ RrColorNew(inst, 0, 0, 0));
+
+ READ_COLOR("menu.separator.color", theme->menu_sep_color,
+ RrColorCopy(theme->menu_color));
+
+ READ_COLOR("menu.bullet.selected.image.color",
+ theme->menu_bullet_selected_color,
+ RrColorCopy(theme->menu_selected_color));
+
+ READ_COLOR("osd.button.unpressed.text.color", theme->osd_unpressed_color,
+ RrColorCopy(theme->osd_text_active_color));
+ READ_COLOR("osd.button.pressed.text.color", theme->osd_pressed_color,
+ RrColorCopy(theme->osd_text_active_color));
+ READ_COLOR("osd.button.focused.text.color", theme->osd_focused_color,
+ RrColorCopy(theme->osd_text_active_color));
+ READ_COLOR("osd.button.pressed.box.color", theme->osd_pressed_lineart,
+ RrColorCopy(theme->titlebut_focused_pressed_color));
+ READ_COLOR("osd.button.focused.box.color", theme->osd_focused_lineart,
+ RrColorCopy(theme->titlebut_hover_focused_color));
+
+ /* load the image masks */
+
+ /* maximize button masks */
+ userdef = TRUE;
+ if (!read_mask(inst, path, theme, "max.xbm", &theme->btn_max->mask)) {
+ guchar data[] = { 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f };
+ theme->btn_max->mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data);
+ userdef = FALSE;
+ }
+ if (!read_mask(inst, path, theme, "max_toggled.xbm",
+ &theme->btn_max->toggled_mask))
+ {
+ if (userdef)
+ theme->btn_max->toggled_mask = RrPixmapMaskCopy(theme->btn_max->mask);
+ else {
+ guchar data[] = { 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f };
+ theme->btn_max->toggled_mask = RrPixmapMaskNew(inst, 6, 6,(gchar*)data);
+ }
+ }
+ READ_MASK_COPY("max_pressed.xbm", theme->btn_max->pressed_mask,
+ theme->btn_max->mask);
+ READ_MASK_COPY("max_disabled.xbm", theme->btn_max->disabled_mask,
+ theme->btn_max->mask);
+ READ_MASK_COPY("max_hover.xbm", theme->btn_max->hover_mask,
+ theme->btn_max->mask);
+ READ_MASK_COPY("max_toggled_pressed.xbm",
+ theme->btn_max->toggled_pressed_mask,
+ theme->btn_max->toggled_mask);
+ READ_MASK_COPY("max_toggled_hover.xbm",
+ theme->btn_max->toggled_hover_mask,
+ theme->btn_max->toggled_mask);
+
+ /* iconify button masks */
+ if (!read_mask(inst, path, theme, "iconify.xbm", &theme->btn_iconify->mask)) {
+ guchar data[] = { 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f };
+ theme->btn_iconify->mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data);
+ }
+ READ_MASK_COPY("iconify_pressed.xbm", theme->btn_iconify->pressed_mask,
+ theme->btn_iconify->mask);
+ READ_MASK_COPY("iconify_disabled.xbm", theme->btn_iconify->disabled_mask,
+ theme->btn_iconify->mask);
+ READ_MASK_COPY("iconify_hover.xbm", theme->btn_iconify->hover_mask,
+ theme->btn_iconify->mask);
+
+ /* all desktops button masks */
+ userdef = TRUE;
+ if (!read_mask(inst, path, theme, "desk.xbm", &theme->btn_desk->mask)) {
+ guchar data[] = { 0x33, 0x33, 0x00, 0x00, 0x33, 0x33 };
+ theme->btn_desk->mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data);
+ userdef = FALSE;
+ }
+ if (!read_mask(inst, path, theme, "desk_toggled.xbm",
+ &theme->btn_desk->toggled_mask)) {
+ if (userdef)
+ theme->btn_desk->toggled_mask = RrPixmapMaskCopy(theme->btn_desk->mask);
+ else {
+ guchar data[] = { 0x00, 0x1e, 0x1a, 0x16, 0x1e, 0x00 };
+ theme->btn_desk->toggled_mask =
+ RrPixmapMaskNew(inst, 6, 6, (gchar*)data);
+ }
+ }
+ READ_MASK_COPY("desk_pressed.xbm", theme->btn_desk->pressed_mask,
+ theme->btn_desk->mask);
+ READ_MASK_COPY("desk_disabled.xbm", theme->btn_desk->disabled_mask,
+ theme->btn_desk->mask);
+ READ_MASK_COPY("desk_hover.xbm", theme->btn_desk->hover_mask, theme->btn_desk->mask);
+ READ_MASK_COPY("desk_toggled_pressed.xbm",
+ theme->btn_desk->toggled_pressed_mask, theme->btn_desk->toggled_mask);
+ READ_MASK_COPY("desk_toggled_hover.xbm", theme->btn_desk->toggled_hover_mask,
+ theme->btn_desk->toggled_mask);
+
+ /* shade button masks */
+ if (!read_mask(inst, path, theme, "shade.xbm", &theme->btn_shade->mask)) {
+ guchar data[] = { 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 };
+ theme->btn_shade->mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data);
+ }
+ READ_MASK_COPY("shade_toggled.xbm", theme->btn_shade->toggled_mask,
+ theme->btn_shade->mask);
+ READ_MASK_COPY("shade_pressed.xbm", theme->btn_shade->pressed_mask,
+ theme->btn_shade->mask);
+ READ_MASK_COPY("shade_disabled.xbm", theme->btn_shade->disabled_mask,
+ theme->btn_shade->mask);
+ READ_MASK_COPY("shade_hover.xbm", theme->btn_shade->hover_mask,
+ theme->btn_shade->mask);
+ READ_MASK_COPY("shade_toggled_pressed.xbm",
+ theme->btn_shade->toggled_pressed_mask,
+ theme->btn_shade->toggled_mask);
+ READ_MASK_COPY("shade_toggled_hover.xbm",
+ theme->btn_shade->toggled_hover_mask,
+ theme->btn_shade->toggled_mask);
+
+ /* close button masks */
+ if (!read_mask(inst, path, theme, "close.xbm", &theme->btn_close->mask)) {
+ guchar data[] = { 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 };
+ theme->btn_close->mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data);
+ }
+ READ_MASK_COPY("close_pressed.xbm", theme->btn_close->pressed_mask,
+ theme->btn_close->mask);
+ READ_MASK_COPY("close_disabled.xbm", theme->btn_close->disabled_mask,
+ theme->btn_close->mask);
+ READ_MASK_COPY("close_hover.xbm", theme->btn_close->hover_mask,
+ theme->btn_close->mask);
+
+ /* submenu bullet mask */
+ if (!read_mask(inst, path, theme, "bullet.xbm", &theme->menu_bullet_mask))
+ {
+ guchar data[] = { 0x01, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x01 };
+ theme->menu_bullet_mask = RrPixmapMaskNew(inst, 4, 7, (gchar*)data);
+ }
+
+ /* up and down arrows */
+ {
+ guchar data[] = { 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00 };
+ theme->down_arrow_mask = RrPixmapMaskNew(inst, 9, 4, (gchar*)data);
+ }
+ {
+ guchar data[] = { 0x10, 0x00, 0x38, 0x00, 0x7c, 0x00, 0xfe, 0x00 };
+ theme->up_arrow_mask = RrPixmapMaskNew(inst, 9, 4, (gchar*)data);
+ }
+
+ /* setup the default window icon */
+ theme->def_win_icon = read_c_image(OB_DEFAULT_ICON_WIDTH,
+ OB_DEFAULT_ICON_HEIGHT,
+ OB_DEFAULT_ICON_pixel_data);
+ theme->def_win_icon_w = OB_DEFAULT_ICON_WIDTH;
+ theme->def_win_icon_h = OB_DEFAULT_ICON_HEIGHT;
+
+ /* the toggled hover mask = the toggled unpressed mask (i.e. no change) */
+ theme->btn_max->toggled_hover_mask =
+ RrPixmapMaskCopy(theme->btn_max->toggled_mask);
+ theme->btn_desk->toggled_hover_mask =
+ RrPixmapMaskCopy(theme->btn_desk->toggled_mask);
+ theme->btn_shade->toggled_hover_mask =
+ RrPixmapMaskCopy(theme->btn_shade->toggled_mask);
+ /* the toggled pressed mask = the toggled unpressed mask (i.e. no change)*/
+ theme->btn_max->toggled_pressed_mask =
+ RrPixmapMaskCopy(theme->btn_max->toggled_mask);
+ theme->btn_desk->toggled_pressed_mask =
+ RrPixmapMaskCopy(theme->btn_desk->toggled_mask);
+ theme->btn_shade->toggled_pressed_mask =
+ RrPixmapMaskCopy(theme->btn_shade->toggled_mask);
+
+ /* read the decoration textures */
+ READ_APPEARANCE("window.active.title.bg", theme->a_focused_title, FALSE);
+ READ_APPEARANCE("window.inactive.title.bg", theme->a_unfocused_title,
+ FALSE);
+ READ_APPEARANCE("window.active.label.bg", theme->a_focused_label, TRUE);
+ READ_APPEARANCE("window.inactive.label.bg", theme->a_unfocused_label,
+ TRUE);
+ READ_APPEARANCE("window.active.handle.bg", theme->a_focused_handle, FALSE);
+ READ_APPEARANCE("window.inactive.handle.bg",theme->a_unfocused_handle,
+ FALSE);
+ READ_APPEARANCE("window.active.grip.bg", theme->a_focused_grip, TRUE);
+ READ_APPEARANCE("window.inactive.grip.bg", theme->a_unfocused_grip, TRUE);
+ READ_APPEARANCE("menu.items.bg", theme->a_menu, FALSE);
+ READ_APPEARANCE("menu.title.bg", theme->a_menu_title, TRUE);
+ READ_APPEARANCE("menu.items.active.bg", theme->a_menu_selected, TRUE);
+
+ theme->a_menu_disabled_selected =
+ RrAppearanceCopy(theme->a_menu_selected);
+
+ /* read appearances for non-decorations (on-screen-display) */
+ if (!read_appearance(db, inst, "osd.bg", theme->osd_bg, FALSE)) {
+ RrAppearanceFree(theme->osd_bg);
+ theme->osd_bg = RrAppearanceCopy(theme->a_focused_title);
+ }
+ if (!read_appearance(db, inst, "osd.active.label.bg",
+ theme->osd_hilite_label, TRUE) &&
+ !read_appearance(db, inst, "osd.label.bg",
+ theme->osd_hilite_label, TRUE)) {
+ RrAppearanceFree(theme->osd_hilite_label);
+ theme->osd_hilite_label = RrAppearanceCopy(theme->a_focused_label);
+ }
+ if (!read_appearance(db, inst, "osd.inactive.label.bg",
+ theme->osd_unhilite_label, TRUE)) {
+ RrAppearanceFree(theme->osd_unhilite_label);
+ theme->osd_unhilite_label = RrAppearanceCopy(theme->a_unfocused_label);
+ }
+ /* osd_hilite_fg can't be parentrel */
+ if (!read_appearance(db, inst, "osd.hilight.bg",
+ theme->osd_hilite_bg, FALSE)) {
+ RrAppearanceFree(theme->osd_hilite_bg);
+ if (theme->a_focused_label->surface.grad != RR_SURFACE_PARENTREL)
+ theme->osd_hilite_bg = RrAppearanceCopy(theme->a_focused_label);
+ else
+ theme->osd_hilite_bg = RrAppearanceCopy(theme->a_focused_title);
+ }
+ /* osd_unhilite_fg can't be parentrel either */
+ if (!read_appearance(db, inst, "osd.unhilight.bg",
+ theme->osd_unhilite_bg, FALSE)) {
+ RrAppearanceFree(theme->osd_unhilite_bg);
+ if (theme->a_unfocused_label->surface.grad != RR_SURFACE_PARENTREL)
+ theme->osd_unhilite_bg=RrAppearanceCopy(theme->a_unfocused_label);
+ else
+ theme->osd_unhilite_bg=RrAppearanceCopy(theme->a_unfocused_title);
+ }
+
+ /* read buttons textures */
+
+ /* bases: unpressed, pressed, disabled */
+ READ_APPEARANCE("window.active.button.unpressed.bg",
+ a_focused_unpressed_tmp, TRUE);
+ READ_APPEARANCE("window.inactive.button.unpressed.bg",
+ a_unfocused_unpressed_tmp, TRUE);
+ READ_APPEARANCE("window.active.button.pressed.bg",
+ a_focused_pressed_tmp, TRUE);
+ READ_APPEARANCE("window.inactive.button.pressed.bg",
+ a_unfocused_pressed_tmp, TRUE);
+ READ_APPEARANCE("window.active.button.disabled.bg",
+ a_disabled_focused_tmp, TRUE);
+ READ_APPEARANCE("window.inactive.button.disabled.bg",
+ a_disabled_unfocused_tmp, TRUE);
+
+ /* hover */
+ READ_APPEARANCE_COPY("window.active.button.hover.bg",
+ a_hover_focused_tmp, TRUE,
+ a_focused_unpressed_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.hover.bg",
+ a_hover_unfocused_tmp, TRUE,
+ a_unfocused_unpressed_tmp);
+
+ /* toggled unpressed */
+ READ_APPEARANCE_("window.active.button.toggled.unpressed.bg",
+ "window.active.button.toggled.bg",
+ a_toggled_focused_unpressed_tmp, TRUE,
+ a_focused_pressed_tmp);
+ READ_APPEARANCE_("window.inactive.button.toggled.unpressed.bg",
+ "window.inactive.button.toggled.bg",
+ a_toggled_unfocused_unpressed_tmp, TRUE,
+ a_unfocused_pressed_tmp);
+
+ /* toggled pressed */
+ READ_APPEARANCE_COPY("window.active.button.toggled.pressed.bg",
+ a_toggled_focused_pressed_tmp, TRUE,
+ a_focused_pressed_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.toggled.pressed.bg",
+ a_toggled_unfocused_pressed_tmp, TRUE,
+ a_unfocused_pressed_tmp);
+
+ /* toggled hover */
+ READ_APPEARANCE_COPY("window.active.button.toggled.hover.bg",
+ a_toggled_hover_focused_tmp, TRUE,
+ a_toggled_focused_unpressed_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.toggled.hover.bg",
+ a_toggled_hover_unfocused_tmp, TRUE,
+ a_toggled_unfocused_unpressed_tmp);
+
+
+ /* now do individual buttons, if specified */
+
+ /* max button */
+ read_button_colors(db, inst, theme, theme->btn_max, "max");
+
+ /* bases: unpressed, pressed, disabled */
+ READ_APPEARANCE_COPY("window.active.button.max.unpressed.bg",
+ theme->btn_max->a_focused_unpressed, TRUE,
+ a_focused_unpressed_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.max.unpressed.bg",
+ theme->btn_max->a_unfocused_unpressed, TRUE,
+ a_unfocused_unpressed_tmp);
+ READ_APPEARANCE_COPY("window.active.button.max.pressed.bg",
+ theme->btn_max->a_focused_pressed, TRUE,
+ a_focused_pressed_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.max.pressed.bg",
+ theme->btn_max->a_unfocused_pressed, TRUE,
+ a_unfocused_pressed_tmp);
+ READ_APPEARANCE_COPY("window.active.button.max.disabled.bg",
+ theme->btn_max->a_disabled_focused, TRUE,
+ a_disabled_focused_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.max.disabled.bg",
+ theme->btn_max->a_disabled_unfocused, TRUE,
+ a_disabled_unfocused_tmp);
+
+ /* hover */
+ READ_APPEARANCE_COPY("window.active.button.max.hover.bg",
+ theme->btn_max->a_hover_focused, TRUE,
+ theme->btn_max->a_focused_unpressed);
+ READ_APPEARANCE_COPY("window.inactive.button.max.hover.bg",
+ theme->btn_max->a_hover_unfocused, TRUE,
+ theme->btn_max->a_unfocused_unpressed);
+
+ /* 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);
+ 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);
+
+ /* 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);
+ READ_APPEARANCE_COPY("window.inactive.button.max.toggled.pressed.bg",
+ theme->btn_max->a_toggled_unfocused_pressed, TRUE,
+ theme->btn_max->a_unfocused_pressed);
+
+ /* 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);
+ 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);
+
+ /* close button */
+ read_button_colors(db, inst, theme, theme->btn_close, "close");
+
+ READ_APPEARANCE_COPY("window.active.button.close.unpressed.bg",
+ theme->btn_close->a_focused_unpressed, TRUE,
+ a_focused_unpressed_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.close.unpressed.bg",
+ theme->btn_close->a_unfocused_unpressed, TRUE,
+ a_unfocused_unpressed_tmp);
+ READ_APPEARANCE_COPY("window.active.button.close.pressed.bg",
+ theme->btn_close->a_focused_pressed, TRUE,
+ a_focused_pressed_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.close.pressed.bg",
+ theme->btn_close->a_unfocused_pressed, TRUE,
+ a_unfocused_pressed_tmp);
+ READ_APPEARANCE_COPY("window.active.button.close.disabled.bg",
+ theme->btn_close->a_disabled_focused, TRUE,
+ a_disabled_focused_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.close.disabled.bg",
+ theme->btn_close->a_disabled_unfocused, TRUE,
+ 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);
+ READ_APPEARANCE_COPY("window.inactive.button.close.hover.bg",
+ theme->btn_close->a_hover_unfocused, TRUE,
+ theme->btn_close->a_unfocused_unpressed);
+
+ /* desk button */
+ read_button_colors(db, inst, theme, theme->btn_desk, "desk");
+
+ /* bases: unpressed, pressed, disabled */
+ READ_APPEARANCE_COPY("window.active.button.desk.unpressed.bg",
+ theme->btn_desk->a_focused_unpressed, TRUE,
+ a_focused_unpressed_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.desk.unpressed.bg",
+ theme->btn_desk->a_unfocused_unpressed, TRUE,
+ a_unfocused_unpressed_tmp);
+ READ_APPEARANCE_COPY("window.active.button.desk.pressed.bg",
+ theme->btn_desk->a_focused_pressed, TRUE,
+ a_focused_pressed_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.desk.pressed.bg",
+ theme->btn_desk->a_unfocused_pressed, TRUE,
+ a_unfocused_pressed_tmp);
+ READ_APPEARANCE_COPY("window.active.button.desk.disabled.bg",
+ theme->btn_desk->a_disabled_focused, TRUE,
+ a_disabled_focused_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.desk.disabled.bg",
+ theme->btn_desk->a_disabled_unfocused, TRUE,
+ a_disabled_unfocused_tmp);
+
+ /* hover */
+ READ_APPEARANCE_COPY("window.active.button.desk.hover.bg",
+ theme->btn_desk->a_hover_focused, TRUE,
+ theme->btn_desk->a_focused_unpressed);
+ READ_APPEARANCE_COPY("window.inactive.button.desk.hover.bg",
+ theme->btn_desk->a_hover_unfocused, TRUE,
+ theme->btn_desk->a_unfocused_unpressed);
+
+ /* 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);
+ 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);
+
+ /* 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);
+ READ_APPEARANCE_COPY("window.inactive.button.desk.toggled.pressed.bg",
+ theme->btn_desk->a_toggled_unfocused_pressed, TRUE,
+ theme->btn_desk->a_unfocused_pressed);
+
+ /* 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);
+ 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);
+
+ /* shade button */
+ read_button_colors(db, inst, theme, theme->btn_shade, "shade");
+
+ /* bases: unpressed, pressed, disabled */
+ READ_APPEARANCE_COPY("window.active.button.shade.unpressed.bg",
+ theme->btn_shade->a_focused_unpressed, TRUE,
+ a_focused_unpressed_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.shade.unpressed.bg",
+ theme->btn_shade->a_unfocused_unpressed, TRUE,
+ a_unfocused_unpressed_tmp);
+ READ_APPEARANCE_COPY("window.active.button.shade.pressed.bg",
+ theme->btn_shade->a_focused_pressed, TRUE,
+ a_focused_pressed_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.shade.pressed.bg",
+ theme->btn_shade->a_unfocused_pressed, TRUE,
+ a_unfocused_pressed_tmp);
+ READ_APPEARANCE_COPY("window.active.button.shade.disabled.bg",
+ theme->btn_shade->a_disabled_focused, TRUE,
+ a_disabled_focused_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.shade.disabled.bg",
+ theme->btn_shade->a_disabled_unfocused, TRUE,
+ a_disabled_unfocused_tmp);
+
+ /* hover */
+ READ_APPEARANCE_COPY("window.active.button.shade.hover.bg",
+ theme->btn_shade->a_hover_focused, TRUE,
+ theme->btn_shade->a_focused_unpressed);
+ READ_APPEARANCE_COPY("window.inactive.button.shade.hover.bg",
+ theme->btn_shade->a_hover_unfocused, TRUE,
+ theme->btn_shade->a_unfocused_unpressed);
+
+ /* 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);
+ 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);
+
+ /* 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);
+ READ_APPEARANCE_COPY("window.inactive.button.shade.toggled.pressed.bg",
+ theme->btn_shade->a_toggled_unfocused_pressed, TRUE,
+ theme->btn_shade->a_unfocused_pressed);
+
+ /* 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);
+ 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);
+
+ /* iconify button */
+ read_button_colors(db, inst, theme, theme->btn_iconify, "iconify");
+
+ READ_APPEARANCE_COPY("window.active.button.iconify.unpressed.bg",
+ theme->btn_iconify->a_focused_unpressed, TRUE,
+ a_focused_unpressed_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.iconify.unpressed.bg",
+ theme->btn_iconify->a_unfocused_unpressed, TRUE,
+ a_unfocused_unpressed_tmp);
+ READ_APPEARANCE_COPY("window.active.button.iconify.pressed.bg",
+ theme->btn_iconify->a_focused_pressed, TRUE,
+ a_focused_pressed_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.iconify.pressed.bg",
+ theme->btn_iconify->a_unfocused_pressed, TRUE,
+ a_unfocused_pressed_tmp);
+ READ_APPEARANCE_COPY("window.active.button.iconify.disabled.bg",
+ theme->btn_iconify->a_disabled_focused, TRUE,
+ a_disabled_focused_tmp);
+ READ_APPEARANCE_COPY("window.inactive.button.iconify.disabled.bg",
+ theme->btn_iconify->a_disabled_unfocused, TRUE,
+ 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);
+ READ_APPEARANCE_COPY("window.inactive.button.iconify.hover.bg",
+ theme->btn_iconify->a_hover_unfocused, TRUE,
+ theme->btn_iconify->a_unfocused_unpressed);
+
+ /* osd buttons */
+ READ_APPEARANCE_COPY("osd.button.unpressed.bg", theme->osd_unpressed_button, TRUE, a_focused_unpressed_tmp);
+ READ_APPEARANCE_COPY_TEXTURES("osd.button.pressed.bg", theme->osd_pressed_button, TRUE, a_focused_pressed_tmp, 5);
+ READ_APPEARANCE_COPY_TEXTURES("osd.button.focused.bg", theme->osd_focused_button, TRUE, a_focused_unpressed_tmp, 5);
+
+ theme->a_icon->surface.grad =
+ theme->a_clear->surface.grad =
+ theme->a_clear_tex->surface.grad =
+ theme->a_menu_text_title->surface.grad =
+ theme->a_menu_normal->surface.grad =
+ theme->a_menu_disabled->surface.grad =
+ theme->a_menu_text_normal->surface.grad =
+ theme->a_menu_text_selected->surface.grad =
+ theme->a_menu_text_disabled->surface.grad =
+ theme->a_menu_text_disabled_selected->surface.grad =
+ theme->a_menu_bullet_normal->surface.grad =
+ theme->a_menu_bullet_selected->surface.grad = RR_SURFACE_PARENTREL;
+
+ /* set up the textures */
+ theme->a_focused_label->texture[0].type = RR_TEXTURE_TEXT;
+ theme->a_focused_label->texture[0].data.text.justify = winjust;
+ theme->a_focused_label->texture[0].data.text.font=theme->win_font_focused;
+ theme->a_focused_label->texture[0].data.text.color =
+ theme->title_focused_color;
+
+ if (read_string(db, "window.active.label.text.font", &str)) {
+ char *p;
+ gint i = 0;
+ gint j;
+ if (strstr(str, "shadow=y")) {
+ if ((p = strstr(str, "shadowoffset=")))
+ i = parse_inline_number(p + strlen("shadowoffset="));
+ else
+ i = 1;
+ theme->a_focused_label->texture[0].data.text.shadow_offset_x = i;
+ theme->a_focused_label->texture[0].data.text.shadow_offset_y = i;
+ }
+ if ((p = strstr(str, "shadowtint=")))
+ {
+ i = parse_inline_number(p + strlen("shadowtint="));
+ j = (i > 0 ? 0 : 255);
+ i = ABS(i*255/100);
+
+ theme->title_focused_shadow_color = RrColorNew(inst, j, j, j);
+ theme->title_focused_shadow_alpha = i;
+ } else {
+ theme->title_focused_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->title_focused_shadow_alpha = 50;
+ }
+ }
+
+ theme->a_focused_label->texture[0].data.text.shadow_color =
+ theme->title_focused_shadow_color;
+ theme->a_focused_label->texture[0].data.text.shadow_alpha =
+ theme->title_focused_shadow_alpha;
+
+ theme->osd_hilite_label->texture[0].type = RR_TEXTURE_TEXT;
+ theme->osd_hilite_label->texture[0].data.text.justify = RR_JUSTIFY_LEFT;
+ theme->osd_hilite_label->texture[0].data.text.font =
+ theme->osd_font_hilite;
+ theme->osd_hilite_label->texture[0].data.text.color =
+ theme->osd_text_active_color;
+
+ if (read_string(db, "osd.active.label.text.font", &str) ||
+ read_string(db, "osd.label.text.font", &str))
+ {
+ char *p;
+ gint i = 0;
+ gint j;
+ if (strstr(str, "shadow=y")) {
+ if ((p = strstr(str, "shadowoffset=")))
+ i = parse_inline_number(p + strlen("shadowoffset="));
+ else
+ i = 1;
+ theme->osd_hilite_label->texture[0].data.text.shadow_offset_x = i;
+ theme->osd_hilite_label->texture[0].data.text.shadow_offset_y = i;
+ }
+ if ((p = strstr(str, "shadowtint=")))
+ {
+ i = parse_inline_number(p + strlen("shadowtint="));
+ j = (i > 0 ? 0 : 255);
+ i = ABS(i*255/100);
+
+ theme->osd_text_active_shadow_color = RrColorNew(inst, j, j, j);
+ theme->osd_text_active_shadow_alpha = i;
+ } else {
+ theme->osd_text_active_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->osd_text_active_shadow_alpha = 50;
+ }
+ } else {
+ /* inherit the font settings from the focused label */
+ theme->osd_hilite_label->texture[0].data.text.shadow_offset_x =
+ theme->a_focused_label->texture[0].data.text.shadow_offset_x;
+ theme->osd_hilite_label->texture[0].data.text.shadow_offset_y =
+ theme->a_focused_label->texture[0].data.text.shadow_offset_y;
+ if (theme->title_focused_shadow_color)
+ theme->osd_text_active_shadow_color =
+ RrColorCopy(theme->title_focused_shadow_color);
+ else
+ theme->osd_text_active_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->osd_text_active_shadow_alpha =
+ theme->title_focused_shadow_alpha;
+ }
+
+ theme->osd_hilite_label->texture[0].data.text.shadow_color =
+ theme->osd_text_active_shadow_color;
+ theme->osd_hilite_label->texture[0].data.text.shadow_alpha =
+ theme->osd_text_active_shadow_alpha;
+
+ theme->osd_unpressed_button->texture[0].type =
+ theme->osd_pressed_button->texture[0].type =
+ theme->osd_focused_button->texture[0].type =
+ RR_TEXTURE_TEXT;
+
+ theme->osd_unpressed_button->texture[0].data.text.justify =
+ theme->osd_pressed_button->texture[0].data.text.justify =
+ theme->osd_focused_button->texture[0].data.text.justify =
+ RR_JUSTIFY_CENTER;
+
+ theme->osd_unpressed_button->texture[0].data.text.font =
+ theme->osd_pressed_button->texture[0].data.text.font =
+ theme->osd_focused_button->texture[0].data.text.font =
+ theme->osd_font_hilite;
+
+ theme->osd_unpressed_button->texture[0].data.text.color =
+ theme->osd_unpressed_color;
+ theme->osd_pressed_button->texture[0].data.text.color =
+ theme->osd_pressed_color;
+ theme->osd_focused_button->texture[0].data.text.color =
+ theme->osd_focused_color;
+
+ theme->osd_pressed_button->texture[1].data.lineart.color =
+ theme->osd_pressed_button->texture[2].data.lineart.color =
+ theme->osd_pressed_button->texture[3].data.lineart.color =
+ theme->osd_pressed_button->texture[4].data.lineart.color =
+ theme->osd_pressed_lineart;
+
+ theme->osd_focused_button->texture[1].data.lineart.color =
+ theme->osd_focused_button->texture[2].data.lineart.color =
+ theme->osd_focused_button->texture[3].data.lineart.color =
+ theme->osd_focused_button->texture[4].data.lineart.color =
+ theme->osd_focused_lineart;
+
+ theme->a_unfocused_label->texture[0].type = RR_TEXTURE_TEXT;
+ theme->a_unfocused_label->texture[0].data.text.justify = winjust;
+ theme->a_unfocused_label->texture[0].data.text.font =
+ theme->win_font_unfocused;
+ theme->a_unfocused_label->texture[0].data.text.color =
+ theme->title_unfocused_color;
+
+ if (read_string(db, "window.inactive.label.text.font", &str)) {
+ char *p;
+ gint i = 0;
+ gint j;
+ if (strstr(str, "shadow=y")) {
+ if ((p = strstr(str, "shadowoffset=")))
+ i = parse_inline_number(p + strlen("shadowoffset="));
+ else
+ i = 1;
+ theme->a_unfocused_label->texture[0].data.text.shadow_offset_x = i;
+ theme->a_unfocused_label->texture[0].data.text.shadow_offset_y = i;
+ }
+ if ((p = strstr(str, "shadowtint=")))
+ {
+ i = parse_inline_number(p + strlen("shadowtint="));
+ j = (i > 0 ? 0 : 255);
+ i = ABS(i*255/100);
+
+ theme->title_unfocused_shadow_color = RrColorNew(inst, j, j, j);
+ theme->title_unfocused_shadow_alpha = i;
+ } else {
+ theme->title_unfocused_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->title_unfocused_shadow_alpha = 50;
+ }
+ }
+
+ theme->a_unfocused_label->texture[0].data.text.shadow_color =
+ theme->title_unfocused_shadow_color;
+ theme->a_unfocused_label->texture[0].data.text.shadow_alpha =
+ theme->title_unfocused_shadow_alpha;
+
+ theme->osd_unhilite_label->texture[0].type = RR_TEXTURE_TEXT;
+ theme->osd_unhilite_label->texture[0].data.text.justify = RR_JUSTIFY_LEFT;
+ theme->osd_unhilite_label->texture[0].data.text.font =
+ theme->osd_font_unhilite;
+ theme->osd_unhilite_label->texture[0].data.text.color =
+ theme->osd_text_inactive_color;
+
+ if (read_string(db, "osd.inactive.label.text.font", &str))
+ {
+ char *p;
+ gint i = 0;
+ gint j;
+ if (strstr(str, "shadow=y")) {
+ if ((p = strstr(str, "shadowoffset=")))
+ i = parse_inline_number(p + strlen("shadowoffset="));
+ else
+ i = 1;
+ theme->osd_unhilite_label->texture[0].data.text.shadow_offset_x=i;
+ theme->osd_unhilite_label->texture[0].data.text.shadow_offset_y=i;
+ }
+ if ((p = strstr(str, "shadowtint=")))
+ {
+ i = parse_inline_number(p + strlen("shadowtint="));
+ j = (i > 0 ? 0 : 255);
+ i = ABS(i*255/100);
+
+ theme->osd_text_inactive_shadow_color = RrColorNew(inst, j, j, j);
+ theme->osd_text_inactive_shadow_alpha = i;
+ } else {
+ theme->osd_text_inactive_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->osd_text_inactive_shadow_alpha = 50;
+ }
+ } else {
+ /* inherit the font settings from the unfocused label */
+ theme->osd_unhilite_label->texture[0].data.text.shadow_offset_x =
+ theme->a_unfocused_label->texture[0].data.text.shadow_offset_x;
+ theme->osd_unhilite_label->texture[0].data.text.shadow_offset_y =
+ theme->a_unfocused_label->texture[0].data.text.shadow_offset_y;
+ if (theme->title_unfocused_shadow_color)
+ theme->osd_text_inactive_shadow_color =
+ RrColorCopy(theme->title_unfocused_shadow_color);
+ else
+ theme->osd_text_inactive_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->osd_text_inactive_shadow_alpha =
+ theme->title_unfocused_shadow_alpha;
+ }
+
+ theme->osd_unhilite_label->texture[0].data.text.shadow_color =
+ theme->osd_text_inactive_shadow_color;
+ theme->osd_unhilite_label->texture[0].data.text.shadow_alpha =
+ theme->osd_text_inactive_shadow_alpha;
+
+ theme->a_menu_text_title->texture[0].type = RR_TEXTURE_TEXT;
+ theme->a_menu_text_title->texture[0].data.text.justify = mtitlejust;
+ theme->a_menu_text_title->texture[0].data.text.font =
+ theme->menu_title_font;
+ theme->a_menu_text_title->texture[0].data.text.color =
+ theme->menu_title_color;
+
+ if (read_string(db, "menu.title.text.font", &str)) {
+ char *p;
+ gint i = 0;
+ gint j;
+ if (strstr(str, "shadow=y")) {
+ if ((p = strstr(str, "shadowoffset=")))
+ i = parse_inline_number(p + strlen("shadowoffset="));
+ else
+ i = 1;
+ theme->a_menu_text_title->texture[0].data.text.shadow_offset_x = i;
+ theme->a_menu_text_title->texture[0].data.text.shadow_offset_y = i;
+ }
+ if ((p = strstr(str, "shadowtint=")))
+ {
+ i = parse_inline_number(p + strlen("shadowtint="));
+ j = (i > 0 ? 0 : 255);
+ i = ABS(i*255/100);
+
+ theme->menu_title_shadow_color = RrColorNew(inst, j, j, j);
+ theme->menu_title_shadow_alpha = i;
+ } else {
+ theme->menu_title_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->menu_title_shadow_alpha = 50;
+ }
+ }
+
+ theme->a_menu_text_title->texture[0].data.text.shadow_color =
+ theme->menu_title_shadow_color;
+ theme->a_menu_text_title->texture[0].data.text.shadow_alpha =
+ theme->menu_title_shadow_alpha;
+
+ theme->a_menu_text_normal->texture[0].type =
+ theme->a_menu_text_selected->texture[0].type =
+ theme->a_menu_text_disabled->texture[0].type =
+ theme->a_menu_text_disabled_selected->texture[0].type =
+ RR_TEXTURE_TEXT;
+ theme->a_menu_text_normal->texture[0].data.text.justify =
+ theme->a_menu_text_selected->texture[0].data.text.justify =
+ theme->a_menu_text_disabled->texture[0].data.text.justify =
+ theme->a_menu_text_disabled_selected->texture[0].data.text.justify =
+ RR_JUSTIFY_LEFT;
+ theme->a_menu_text_normal->texture[0].data.text.font =
+ theme->a_menu_text_selected->texture[0].data.text.font =
+ theme->a_menu_text_disabled->texture[0].data.text.font =
+ theme->a_menu_text_disabled_selected->texture[0].data.text.font =
+ theme->menu_font;
+ theme->a_menu_text_normal->texture[0].data.text.color = theme->menu_color;
+ theme->a_menu_text_selected->texture[0].data.text.color =
+ theme->menu_selected_color;
+ theme->a_menu_text_disabled->texture[0].data.text.color =
+ theme->menu_disabled_color;
+ theme->a_menu_text_disabled_selected->texture[0].data.text.color =
+ theme->menu_disabled_selected_color;
+
+ if (read_string(db, "menu.items.font", &str)) {
+ char *p;
+ gint i = 0;
+ gint j;
+ if (strstr(str, "shadow=y")) {
+ if ((p = strstr(str, "shadowoffset=")))
+ i = parse_inline_number(p + strlen("shadowoffset="));
+ else
+ i = 1;
+ theme->a_menu_text_normal->
+ texture[0].data.text.shadow_offset_x = i;
+ theme->a_menu_text_normal->
+ texture[0].data.text.shadow_offset_y = i;
+ theme->a_menu_text_selected->
+ texture[0].data.text.shadow_offset_x = i;
+ theme->a_menu_text_selected->
+ texture[0].data.text.shadow_offset_y = i;
+ theme->a_menu_text_disabled->
+ texture[0].data.text.shadow_offset_x = i;
+ theme->a_menu_text_disabled->
+ texture[0].data.text.shadow_offset_y = i;
+ theme->a_menu_text_disabled_selected->
+ texture[0].data.text.shadow_offset_x = i;
+ theme->a_menu_text_disabled_selected->
+ texture[0].data.text.shadow_offset_y = i;
+ }
+ if ((p = strstr(str, "shadowtint=")))
+ {
+ i = parse_inline_number(p + strlen("shadowtint="));
+ j = (i > 0 ? 0 : 255);
+ i = ABS(i*255/100);
+
+ theme->menu_text_normal_shadow_color = RrColorNew(inst, j, j, j);
+ theme->menu_text_selected_shadow_color = RrColorNew(inst, j, j, j);
+ theme->menu_text_disabled_shadow_color = RrColorNew(inst, j, j, j);
+ theme->menu_text_normal_shadow_alpha = i;
+ theme->menu_text_selected_shadow_alpha = i;
+ theme->menu_text_disabled_shadow_alpha = i;
+ theme->menu_text_disabled_selected_shadow_alpha = i;
+ } else {
+ theme->menu_text_normal_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->menu_text_selected_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->menu_text_disabled_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->menu_text_normal_shadow_alpha = 50;
+ theme->menu_text_selected_shadow_alpha = 50;
+ theme->menu_text_disabled_selected_shadow_alpha = 50;
+ }
+ }
+
+ theme->a_menu_text_normal->texture[0].data.text.shadow_color =
+ theme->menu_text_normal_shadow_color;
+ theme->a_menu_text_normal->texture[0].data.text.shadow_alpha =
+ theme->menu_text_normal_shadow_alpha;
+ theme->a_menu_text_selected->texture[0].data.text.shadow_color =
+ theme->menu_text_selected_shadow_color;
+ theme->a_menu_text_selected->texture[0].data.text.shadow_alpha =
+ theme->menu_text_selected_shadow_alpha;
+ theme->a_menu_text_disabled->texture[0].data.text.shadow_color =
+ theme->menu_text_disabled_shadow_color;
+ theme->a_menu_text_disabled->texture[0].data.text.shadow_alpha =
+ theme->menu_text_disabled_shadow_alpha;
+ theme->a_menu_text_disabled_selected->texture[0].data.text.shadow_color =
+ theme->menu_text_disabled_shadow_color;
+ theme->a_menu_text_disabled_selected->texture[0].data.text.shadow_alpha =
+ theme->menu_text_disabled_shadow_alpha;
+
+ theme->btn_max->a_disabled_focused->texture[0].type =
+ theme->btn_max->a_disabled_unfocused->texture[0].type =
+ theme->btn_max->a_hover_focused->texture[0].type =
+ theme->btn_max->a_hover_unfocused->texture[0].type =
+ theme->btn_max->a_toggled_hover_focused->texture[0].type =
+ theme->btn_max->a_toggled_hover_unfocused->texture[0].type =
+ theme->btn_max->a_toggled_focused_unpressed->texture[0].type =
+ theme->btn_max->a_toggled_unfocused_unpressed->texture[0].type =
+ theme->btn_max->a_toggled_focused_pressed->texture[0].type =
+ theme->btn_max->a_toggled_unfocused_pressed->texture[0].type =
+ theme->btn_max->a_focused_unpressed->texture[0].type =
+ theme->btn_max->a_focused_pressed->texture[0].type =
+ theme->btn_max->a_unfocused_unpressed->texture[0].type =
+ theme->btn_max->a_unfocused_pressed->texture[0].type =
+ theme->btn_close->a_disabled_focused->texture[0].type =
+ theme->btn_close->a_disabled_unfocused->texture[0].type =
+ theme->btn_close->a_hover_focused->texture[0].type =
+ theme->btn_close->a_hover_unfocused->texture[0].type =
+ theme->btn_close->a_focused_unpressed->texture[0].type =
+ theme->btn_close->a_focused_pressed->texture[0].type =
+ theme->btn_close->a_unfocused_unpressed->texture[0].type =
+ theme->btn_close->a_unfocused_pressed->texture[0].type =
+ theme->btn_desk->a_disabled_focused->texture[0].type =
+ theme->btn_desk->a_disabled_unfocused->texture[0].type =
+ theme->btn_desk->a_hover_focused->texture[0].type =
+ theme->btn_desk->a_hover_unfocused->texture[0].type =
+ theme->btn_desk->a_toggled_hover_focused->texture[0].type =
+ theme->btn_desk->a_toggled_hover_unfocused->texture[0].type =
+ theme->btn_desk->a_toggled_focused_unpressed->texture[0].type =
+ theme->btn_desk->a_toggled_unfocused_unpressed->texture[0].type =
+ theme->btn_desk->a_toggled_focused_pressed->texture[0].type =
+ theme->btn_desk->a_toggled_unfocused_pressed->texture[0].type =
+ theme->btn_desk->a_focused_unpressed->texture[0].type =
+ theme->btn_desk->a_focused_pressed->texture[0].type =
+ theme->btn_desk->a_unfocused_unpressed->texture[0].type =
+ theme->btn_desk->a_unfocused_pressed->texture[0].type =
+ theme->btn_shade->a_disabled_focused->texture[0].type =
+ theme->btn_shade->a_disabled_unfocused->texture[0].type =
+ theme->btn_shade->a_hover_focused->texture[0].type =
+ theme->btn_shade->a_hover_unfocused->texture[0].type =
+ theme->btn_shade->a_toggled_hover_focused->texture[0].type =
+ theme->btn_shade->a_toggled_hover_unfocused->texture[0].type =
+ theme->btn_shade->a_toggled_focused_unpressed->texture[0].type =
+ theme->btn_shade->a_toggled_unfocused_unpressed->texture[0].type =
+ theme->btn_shade->a_toggled_focused_pressed->texture[0].type =
+ theme->btn_shade->a_toggled_unfocused_pressed->texture[0].type =
+ theme->btn_shade->a_focused_unpressed->texture[0].type =
+ theme->btn_shade->a_focused_pressed->texture[0].type =
+ theme->btn_shade->a_unfocused_unpressed->texture[0].type =
+ theme->btn_shade->a_unfocused_pressed->texture[0].type =
+ theme->btn_iconify->a_disabled_focused->texture[0].type =
+ theme->btn_iconify->a_disabled_unfocused->texture[0].type =
+ theme->btn_iconify->a_hover_focused->texture[0].type =
+ theme->btn_iconify->a_hover_unfocused->texture[0].type =
+ theme->btn_iconify->a_focused_unpressed->texture[0].type =
+ theme->btn_iconify->a_focused_pressed->texture[0].type =
+ theme->btn_iconify->a_unfocused_unpressed->texture[0].type =
+ theme->btn_iconify->a_unfocused_pressed->texture[0].type =
+ theme->a_menu_bullet_normal->texture[0].type =
+ theme->a_menu_bullet_selected->texture[0].type = RR_TEXTURE_MASK;
+
+ theme->btn_max->a_disabled_focused->texture[0].data.mask.mask =
+ theme->btn_max->a_disabled_unfocused->texture[0].data.mask.mask =
+ theme->btn_max->disabled_mask;
+ theme->btn_max->a_hover_focused->texture[0].data.mask.mask =
+ theme->btn_max->a_hover_unfocused->texture[0].data.mask.mask =
+ theme->btn_max->hover_mask;
+ theme->btn_max->a_focused_pressed->texture[0].data.mask.mask =
+ theme->btn_max->a_unfocused_pressed->texture[0].data.mask.mask =
+ theme->btn_max->pressed_mask;
+ theme->btn_max->a_focused_unpressed->texture[0].data.mask.mask =
+ theme->btn_max->a_unfocused_unpressed->texture[0].data.mask.mask =
+ theme->btn_max->mask;
+ theme->btn_max->a_toggled_hover_focused->texture[0].data.mask.mask =
+ theme->btn_max->a_toggled_hover_unfocused->texture[0].data.mask.mask =
+ theme->btn_max->toggled_hover_mask;
+ theme->btn_max->a_toggled_focused_unpressed->texture[0].data.mask.mask =
+ theme->btn_max->a_toggled_unfocused_unpressed->
+ texture[0].data.mask.mask = theme->btn_max->toggled_mask;
+ theme->btn_max->a_toggled_focused_pressed->texture[0].data.mask.mask =
+ theme->btn_max->a_toggled_unfocused_pressed->texture[0].data.mask.mask
+ = theme->btn_max->toggled_pressed_mask;
+ theme->btn_close->a_disabled_focused->texture[0].data.mask.mask =
+ theme->btn_close->a_disabled_unfocused->texture[0].data.mask.mask =
+ theme->btn_close->disabled_mask;
+ theme->btn_close->a_hover_focused->texture[0].data.mask.mask =
+ theme->btn_close->a_hover_unfocused->texture[0].data.mask.mask =
+ theme->btn_close->hover_mask;
+ theme->btn_close->a_focused_pressed->texture[0].data.mask.mask =
+ theme->btn_close->a_unfocused_pressed->texture[0].data.mask.mask =
+ theme->btn_close->pressed_mask;
+ theme->btn_close->a_focused_unpressed->texture[0].data.mask.mask =
+ theme->btn_close->a_unfocused_unpressed->texture[0].data.mask.mask =
+ theme->btn_close->mask;
+ theme->btn_desk->a_disabled_focused->texture[0].data.mask.mask =
+ theme->btn_desk->a_disabled_unfocused->texture[0].data.mask.mask =
+ theme->btn_desk->disabled_mask;
+ theme->btn_desk->a_hover_focused->texture[0].data.mask.mask =
+ theme->btn_desk->a_hover_unfocused->texture[0].data.mask.mask =
+ theme->btn_desk->hover_mask;
+ theme->btn_desk->a_focused_pressed->texture[0].data.mask.mask =
+ theme->btn_desk->a_unfocused_pressed->texture[0].data.mask.mask =
+ theme->btn_desk->pressed_mask;
+ theme->btn_desk->a_focused_unpressed->texture[0].data.mask.mask =
+ theme->btn_desk->a_unfocused_unpressed->texture[0].data.mask.mask =
+ theme->btn_desk->mask;
+ theme->btn_desk->a_toggled_hover_focused->texture[0].data.mask.mask =
+ theme->btn_desk->a_toggled_hover_unfocused->texture[0].data.mask.mask =
+ theme->btn_desk->toggled_hover_mask;
+ theme->btn_desk->a_toggled_focused_unpressed->texture[0].data.mask.mask =
+ theme->btn_desk->a_toggled_unfocused_unpressed->
+ texture[0].data.mask.mask = theme->btn_desk->toggled_mask;
+ theme->btn_desk->a_toggled_focused_pressed->texture[0].data.mask.mask =
+ theme->btn_desk->a_toggled_unfocused_pressed->texture[0].data.mask.mask
+ = theme->btn_desk->toggled_pressed_mask;
+ theme->btn_shade->a_disabled_focused->texture[0].data.mask.mask =
+ theme->btn_shade->a_disabled_unfocused->texture[0].data.mask.mask =
+ theme->btn_shade->disabled_mask;
+ theme->btn_shade->a_hover_focused->texture[0].data.mask.mask =
+ theme->btn_shade->a_hover_unfocused->texture[0].data.mask.mask =
+ theme->btn_shade->hover_mask;
+ theme->btn_shade->a_focused_pressed->texture[0].data.mask.mask =
+ theme->btn_shade->a_unfocused_pressed->texture[0].data.mask.mask =
+ theme->btn_shade->pressed_mask;
+ theme->btn_shade->a_focused_unpressed->texture[0].data.mask.mask =
+ theme->btn_shade->a_unfocused_unpressed->texture[0].data.mask.mask =
+ theme->btn_shade->mask;
+ theme->btn_shade->a_toggled_hover_focused->texture[0].data.mask.mask =
+ theme->btn_shade->a_toggled_hover_unfocused->texture[0].data.mask.mask
+ = theme->btn_shade->toggled_hover_mask;
+ theme->btn_shade->a_toggled_focused_unpressed->texture[0].data.mask.mask =
+ theme->btn_shade->a_toggled_unfocused_unpressed->
+ texture[0].data.mask.mask = theme->btn_shade->toggled_mask;
+ theme->btn_shade->a_toggled_focused_pressed->texture[0].data.mask.mask =
+ theme->btn_shade->a_toggled_unfocused_pressed->
+ texture[0].data.mask.mask = theme->btn_shade->toggled_pressed_mask;
+ theme->btn_iconify->a_disabled_focused->texture[0].data.mask.mask =
+ theme->btn_iconify->a_disabled_unfocused->texture[0].data.mask.mask =
+ theme->btn_iconify->disabled_mask;
+ theme->btn_iconify->a_hover_focused->texture[0].data.mask.mask =
+ theme->btn_iconify->a_hover_unfocused->texture[0].data.mask.mask =
+ theme->btn_iconify->hover_mask;
+ theme->btn_iconify->a_focused_pressed->texture[0].data.mask.mask =
+ theme->btn_iconify->a_unfocused_pressed->texture[0].data.mask.mask =
+ theme->btn_iconify->pressed_mask;
+ theme->btn_iconify->a_focused_unpressed->texture[0].data.mask.mask =
+ theme->btn_iconify->a_unfocused_unpressed->texture[0].data.mask.mask =
+ theme->btn_iconify->mask;
+ theme->a_menu_bullet_normal->texture[0].data.mask.mask =
+ theme->a_menu_bullet_selected->texture[0].data.mask.mask =
+ theme->menu_bullet_mask;
+ theme->btn_max->a_disabled_focused->texture[0].data.mask.color =
+ theme->btn_max->disabled_focused_color;
+ theme->btn_close->a_disabled_focused->texture[0].data.mask.color =
+ theme->btn_close->disabled_focused_color;
+ theme->btn_desk->a_disabled_focused->texture[0].data.mask.color =
+ theme->btn_desk->disabled_focused_color;
+ theme->btn_shade->a_disabled_focused->texture[0].data.mask.color =
+ theme->btn_shade->disabled_focused_color;
+ theme->btn_iconify->a_disabled_focused->texture[0].data.mask.color =
+ theme->btn_iconify->disabled_focused_color;
+ theme->btn_max->a_disabled_unfocused->texture[0].data.mask.color =
+ theme->btn_max->disabled_unfocused_color;
+ theme->btn_close->a_disabled_unfocused->texture[0].data.mask.color =
+ theme->btn_close->disabled_unfocused_color;
+ theme->btn_desk->a_disabled_unfocused->texture[0].data.mask.color =
+ theme->btn_desk->disabled_unfocused_color;
+ theme->btn_shade->a_disabled_unfocused->texture[0].data.mask.color =
+ theme->btn_shade->disabled_unfocused_color;
+ theme->btn_iconify->a_disabled_unfocused->texture[0].data.mask.color =
+ theme->btn_iconify->disabled_unfocused_color;
+ theme->btn_max->a_hover_focused->texture[0].data.mask.color =
+ theme->btn_max->hover_focused_color;
+ theme->btn_close->a_hover_focused->texture[0].data.mask.color =
+ theme->btn_close->hover_focused_color;
+ theme->btn_desk->a_hover_focused->texture[0].data.mask.color =
+ theme->btn_desk->hover_focused_color;
+ theme->btn_shade->a_hover_focused->texture[0].data.mask.color =
+ theme->btn_shade->hover_focused_color;
+ theme->btn_iconify->a_hover_focused->texture[0].data.mask.color =
+ theme->btn_iconify->hover_focused_color;
+ theme->btn_max->a_hover_unfocused->texture[0].data.mask.color =
+ theme->btn_max->hover_unfocused_color;
+ theme->btn_close->a_hover_unfocused->texture[0].data.mask.color =
+ theme->btn_close->hover_unfocused_color;
+ theme->btn_desk->a_hover_unfocused->texture[0].data.mask.color =
+ theme->btn_desk->hover_unfocused_color;
+ theme->btn_shade->a_hover_unfocused->texture[0].data.mask.color =
+ theme->btn_shade->hover_unfocused_color;
+ theme->btn_iconify->a_hover_unfocused->texture[0].data.mask.color =
+ theme->btn_iconify->hover_unfocused_color;
+ theme->btn_max->a_toggled_hover_focused->texture[0].data.mask.color =
+ theme->btn_max->toggled_hover_focused_color;
+ theme->btn_desk->a_toggled_hover_focused->texture[0].data.mask.color =
+ theme->btn_desk->toggled_hover_focused_color;
+ theme->btn_shade->a_toggled_hover_focused->texture[0].data.mask.color =
+ theme->btn_shade->toggled_hover_focused_color;
+ theme->btn_max->a_toggled_hover_unfocused->texture[0].data.mask.color =
+ theme->btn_max->toggled_hover_unfocused_color;
+ theme->btn_desk->a_toggled_hover_unfocused->texture[0].data.mask.color =
+ theme->btn_desk->toggled_hover_unfocused_color;
+ theme->btn_shade->a_toggled_hover_unfocused->texture[0].data.mask.color =
+ theme->btn_shade->toggled_hover_unfocused_color;
+ theme->btn_max->a_toggled_focused_unpressed->texture[0].data.mask.color =
+ theme->btn_max->toggled_focused_unpressed_color;
+ theme->btn_desk->a_toggled_focused_unpressed->texture[0].data.mask.color =
+ theme->btn_desk->toggled_focused_unpressed_color;
+ theme->btn_shade->a_toggled_focused_unpressed->texture[0].data.mask.color =
+ theme->btn_shade->toggled_focused_unpressed_color;
+ theme->btn_max->a_toggled_unfocused_unpressed->texture[0].data.mask.color =
+ theme->btn_max->toggled_unfocused_unpressed_color;
+ theme->btn_desk->a_toggled_unfocused_unpressed->texture[0].data.mask.color
+ = theme->btn_desk->toggled_unfocused_unpressed_color;
+ theme->btn_shade->a_toggled_unfocused_unpressed->texture[0].data.mask.color
+ = theme->btn_shade->toggled_unfocused_unpressed_color;
+ theme->btn_max->a_toggled_focused_pressed->texture[0].data.mask.color =
+ theme->btn_max->toggled_focused_pressed_color;
+ theme->btn_desk->a_toggled_focused_pressed->texture[0].data.mask.color =
+ theme->btn_desk->toggled_focused_pressed_color;
+ theme->btn_shade->a_toggled_focused_pressed->texture[0].data.mask.color =
+ theme->btn_shade->toggled_focused_pressed_color;
+ theme->btn_max->a_toggled_unfocused_pressed->texture[0].data.mask.color =
+ theme->btn_max->toggled_unfocused_pressed_color;
+ theme->btn_desk->a_toggled_unfocused_pressed->texture[0].data.mask.color =
+ theme->btn_desk->toggled_unfocused_pressed_color;
+ theme->btn_shade->a_toggled_unfocused_pressed->texture[0].data.mask.color =
+ theme->btn_shade->toggled_unfocused_pressed_color;
+ theme->btn_max->a_focused_unpressed->texture[0].data.mask.color =
+ theme->btn_max->focused_unpressed_color;
+ theme->btn_close->a_focused_unpressed->texture[0].data.mask.color =
+ theme->btn_close->focused_unpressed_color;
+ theme->btn_desk->a_focused_unpressed->texture[0].data.mask.color =
+ theme->btn_desk->focused_unpressed_color;
+ theme->btn_shade->a_focused_unpressed->texture[0].data.mask.color =
+ theme->btn_shade->focused_unpressed_color;
+ theme->btn_iconify->a_focused_unpressed->texture[0].data.mask.color =
+ theme->btn_iconify->focused_unpressed_color;
+ theme->btn_max->a_focused_pressed->texture[0].data.mask.color =
+ theme->btn_max->focused_pressed_color;
+ theme->btn_close->a_focused_pressed->texture[0].data.mask.color =
+ theme->btn_close->focused_pressed_color;
+ theme->btn_desk->a_focused_pressed->texture[0].data.mask.color =
+ theme->btn_desk->focused_pressed_color;
+ theme->btn_shade->a_focused_pressed->texture[0].data.mask.color =
+ theme->btn_shade->focused_pressed_color;
+ theme->btn_iconify->a_focused_pressed->texture[0].data.mask.color =
+ theme->btn_iconify->focused_pressed_color;
+ theme->btn_max->a_unfocused_unpressed->texture[0].data.mask.color =
+ theme->btn_max->unfocused_unpressed_color;
+ theme->btn_close->a_unfocused_unpressed->texture[0].data.mask.color =
+ theme->btn_close->unfocused_unpressed_color;
+ theme->btn_desk->a_unfocused_unpressed->texture[0].data.mask.color =
+ theme->btn_desk->unfocused_unpressed_color;
+ theme->btn_shade->a_unfocused_unpressed->texture[0].data.mask.color =
+ theme->btn_shade->unfocused_unpressed_color;
+ theme->btn_iconify->a_unfocused_unpressed->texture[0].data.mask.color =
+ theme->btn_iconify->unfocused_unpressed_color;
+ theme->btn_max->a_unfocused_pressed->texture[0].data.mask.color =
+ theme->btn_max->unfocused_pressed_color;
+ theme->btn_close->a_unfocused_pressed->texture[0].data.mask.color =
+ theme->btn_close->unfocused_pressed_color;
+ theme->btn_desk->a_unfocused_pressed->texture[0].data.mask.color =
+ theme->btn_desk->unfocused_pressed_color;
+ theme->btn_shade->a_unfocused_pressed->texture[0].data.mask.color =
+ theme->btn_shade->unfocused_pressed_color;
+ theme->btn_iconify->a_unfocused_pressed->texture[0].data.mask.color =
+ theme->btn_iconify->unfocused_pressed_color;
+ theme->a_menu_bullet_normal->texture[0].data.mask.color =
+ theme->menu_bullet_color;
+ theme->a_menu_bullet_selected->texture[0].data.mask.color =
+ theme->menu_bullet_selected_color;
+
+ g_free(path);
+ XrmDestroyDatabase(db);
+
+ /* set the font heights */
+ theme->win_font_height = RrFontHeight
+ (theme->win_font_focused,
+ theme->a_focused_label->texture[0].data.text.shadow_offset_y);
+ theme->win_font_height =
+ MAX(theme->win_font_height,
+ RrFontHeight
+ (theme->win_font_focused,
+ theme->a_unfocused_label->texture[0].data.text.shadow_offset_y));
+ theme->menu_title_font_height = RrFontHeight
+ (theme->menu_title_font,
+ theme->a_menu_text_title->texture[0].data.text.shadow_offset_y);
+ theme->menu_font_height = RrFontHeight
+ (theme->menu_font,
+ theme->a_menu_text_normal->texture[0].data.text.shadow_offset_y);
+
+ /* calculate some last extents */
+ {
+ gint ft, fb, fl, fr, ut, ub, ul, ur;
+
+ RrMargins(theme->a_focused_label, &fl, &ft, &fr, &fb);
+ RrMargins(theme->a_unfocused_label, &ul, &ut, &ur, &ub);
+ theme->label_height = theme->win_font_height + MAX(ft + fb, ut + ub);
+ theme->label_height += theme->label_height % 2;
+
+ /* this would be nice I think, since padding.width can now be 0,
+ but it breaks frame.c horribly and I don't feel like fixing that
+ right now, so if anyone complains, here is how to keep text from
+ going over the title's bevel/border with a padding.width of 0 and a
+ bevelless/borderless label
+ RrMargins(theme->a_focused_title, &fl, &ft, &fr, &fb);
+ RrMargins(theme->a_unfocused_title, &ul, &ut, &ur, &ub);
+ theme->title_height = theme->label_height +
+ MAX(MAX(theme->padding * 2, ft + fb),
+ MAX(theme->padding * 2, ut + ub));
+ */
+ theme->title_height = theme->label_height + theme->paddingy * 2;
+
+ RrMargins(theme->a_menu_title, &ul, &ut, &ur, &ub);
+ theme->menu_title_label_height = theme->menu_title_font_height+ut+ub;
+ theme->menu_title_height = theme->menu_title_label_height +
+ theme->paddingy * 2;
+ }
+ theme->button_size = theme->label_height - 2;
+ theme->grip_width = 25;
+
+ RrAppearanceFree(a_disabled_focused_tmp);
+ RrAppearanceFree(a_disabled_unfocused_tmp);
+ RrAppearanceFree(a_hover_focused_tmp);
+ RrAppearanceFree(a_hover_unfocused_tmp);
+ RrAppearanceFree(a_focused_unpressed_tmp);
+ RrAppearanceFree(a_focused_pressed_tmp);
+ RrAppearanceFree(a_unfocused_unpressed_tmp);
+ RrAppearanceFree(a_unfocused_pressed_tmp);
+ RrAppearanceFree(a_toggled_hover_focused_tmp);
+ RrAppearanceFree(a_toggled_hover_unfocused_tmp);
+ RrAppearanceFree(a_toggled_focused_unpressed_tmp);
+ RrAppearanceFree(a_toggled_focused_pressed_tmp);
+ RrAppearanceFree(a_toggled_unfocused_unpressed_tmp);
+ RrAppearanceFree(a_toggled_unfocused_pressed_tmp);
+
+ return theme;
+}
+
+void RrThemeFree(RrTheme *theme)
+{
+ if (theme) {
+ g_free(theme->name);
+
+ RrButtonFree(theme->btn_max);
+ RrButtonFree(theme->btn_close);
+ RrButtonFree(theme->btn_desk);
+ RrButtonFree(theme->btn_shade);
+ RrButtonFree(theme->btn_iconify);
+
+ RrColorFree(theme->menu_border_color);
+ RrColorFree(theme->osd_border_color);
+ RrColorFree(theme->frame_focused_border_color);
+ RrColorFree(theme->frame_undecorated_focused_border_color);
+ RrColorFree(theme->frame_unfocused_border_color);
+ RrColorFree(theme->frame_undecorated_unfocused_border_color);
+ RrColorFree(theme->title_separator_focused_color);
+ RrColorFree(theme->title_separator_unfocused_color);
+ RrColorFree(theme->cb_unfocused_color);
+ RrColorFree(theme->cb_focused_color);
+ RrColorFree(theme->title_focused_color);
+ RrColorFree(theme->title_unfocused_color);
+ RrColorFree(theme->titlebut_disabled_focused_color);
+ RrColorFree(theme->titlebut_disabled_unfocused_color);
+ RrColorFree(theme->titlebut_hover_focused_color);
+ RrColorFree(theme->titlebut_hover_unfocused_color);
+ RrColorFree(theme->titlebut_toggled_hover_focused_color);
+ RrColorFree(theme->titlebut_toggled_hover_unfocused_color);
+ RrColorFree(theme->titlebut_toggled_focused_pressed_color);
+ RrColorFree(theme->titlebut_toggled_unfocused_pressed_color);
+ RrColorFree(theme->titlebut_toggled_focused_unpressed_color);
+ RrColorFree(theme->titlebut_toggled_unfocused_unpressed_color);
+ RrColorFree(theme->titlebut_focused_pressed_color);
+ RrColorFree(theme->titlebut_unfocused_pressed_color);
+ RrColorFree(theme->titlebut_focused_unpressed_color);
+ RrColorFree(theme->titlebut_unfocused_unpressed_color);
+ RrColorFree(theme->menu_title_color);
+ RrColorFree(theme->menu_sep_color);
+ RrColorFree(theme->menu_color);
+ RrColorFree(theme->menu_bullet_color);
+ RrColorFree(theme->menu_bullet_selected_color);
+ RrColorFree(theme->menu_selected_color);
+ RrColorFree(theme->menu_disabled_color);
+ RrColorFree(theme->menu_disabled_selected_color);
+ RrColorFree(theme->title_focused_shadow_color);
+ RrColorFree(theme->title_unfocused_shadow_color);
+ RrColorFree(theme->osd_text_active_color);
+ RrColorFree(theme->osd_text_inactive_color);
+ RrColorFree(theme->osd_text_active_shadow_color);
+ RrColorFree(theme->osd_text_inactive_shadow_color);
+ RrColorFree(theme->osd_pressed_color);
+ RrColorFree(theme->osd_unpressed_color);
+ RrColorFree(theme->osd_focused_color);
+ RrColorFree(theme->osd_pressed_lineart);
+ RrColorFree(theme->osd_focused_lineart);
+ RrColorFree(theme->menu_title_shadow_color);
+ RrColorFree(theme->menu_text_normal_shadow_color);
+ RrColorFree(theme->menu_text_selected_shadow_color);
+ RrColorFree(theme->menu_text_disabled_shadow_color);
+ RrColorFree(theme->menu_text_disabled_selected_shadow_color);
+
+ g_free(theme->def_win_icon);
+
+ RrPixmapMaskFree(theme->menu_bullet_mask);
+ RrPixmapMaskFree(theme->down_arrow_mask);
+ RrPixmapMaskFree(theme->up_arrow_mask);
+
+ RrFontClose(theme->win_font_focused);
+ RrFontClose(theme->win_font_unfocused);
+ RrFontClose(theme->menu_title_font);
+ RrFontClose(theme->menu_font);
+ RrFontClose(theme->osd_font_hilite);
+ RrFontClose(theme->osd_font_unhilite);
+
+ RrAppearanceFree(theme->a_focused_grip);
+ RrAppearanceFree(theme->a_unfocused_grip);
+ RrAppearanceFree(theme->a_focused_title);
+ RrAppearanceFree(theme->a_unfocused_title);
+ RrAppearanceFree(theme->a_focused_label);
+ RrAppearanceFree(theme->a_unfocused_label);
+ RrAppearanceFree(theme->a_icon);
+ RrAppearanceFree(theme->a_focused_handle);
+ RrAppearanceFree(theme->a_unfocused_handle);
+ RrAppearanceFree(theme->a_menu);
+ RrAppearanceFree(theme->a_menu_title);
+ RrAppearanceFree(theme->a_menu_text_title);
+ RrAppearanceFree(theme->a_menu_normal);
+ RrAppearanceFree(theme->a_menu_selected);
+ RrAppearanceFree(theme->a_menu_disabled);
+ RrAppearanceFree(theme->a_menu_disabled_selected);
+ RrAppearanceFree(theme->a_menu_text_normal);
+ RrAppearanceFree(theme->a_menu_text_selected);
+ RrAppearanceFree(theme->a_menu_text_disabled);
+ RrAppearanceFree(theme->a_menu_text_disabled_selected);
+ RrAppearanceFree(theme->a_menu_bullet_normal);
+ RrAppearanceFree(theme->a_menu_bullet_selected);
+ RrAppearanceFree(theme->a_clear);
+ RrAppearanceFree(theme->a_clear_tex);
+ RrAppearanceFree(theme->osd_bg);
+ RrAppearanceFree(theme->osd_hilite_bg);
+ RrAppearanceFree(theme->osd_hilite_label);
+ RrAppearanceFree(theme->osd_unhilite_bg);
+ RrAppearanceFree(theme->osd_unhilite_label);
+ RrAppearanceFree(theme->osd_pressed_button);
+ RrAppearanceFree(theme->osd_unpressed_button);
+ RrAppearanceFree(theme->osd_focused_button);
+
+ g_slice_free(RrTheme, theme);
+ }
+}
+
+static XrmDatabase loaddb(const gchar *name, gchar **path)
+{
+ GSList *it;
+ XrmDatabase db = NULL;
+ gchar *s;
+
+ if (name[0] == '/') {
+ s = g_build_filename(name, "openbox-3", "themerc", NULL);
+ if ((db = XrmGetFileDatabase(s)))
+ *path = g_path_get_dirname(s);
+ g_free(s);
+ } else {
+ ObtPaths *p;
+
+ p = obt_paths_new();
+
+ /* XXX backwards compatibility, remove me sometime later */
+ s = g_build_filename(g_get_home_dir(), ".themes", name,
+ "openbox-3", "themerc", NULL);
+ if ((db = XrmGetFileDatabase(s)))
+ *path = g_path_get_dirname(s);
+ g_free(s);
+
+ for (it = obt_paths_data_dirs(p); !db && it; it = g_slist_next(it))
+ {
+ s = g_build_filename(it->data, "themes", name,
+ "openbox-3", "themerc", NULL);
+ if ((db = XrmGetFileDatabase(s)))
+ *path = g_path_get_dirname(s);
+ g_free(s);
+ }
+
+ obt_paths_unref(p);
+ }
+
+ if (db == NULL) {
+ s = g_build_filename(name, "themerc", NULL);
+ if ((db = XrmGetFileDatabase(s)))
+ *path = g_path_get_dirname(s);
+ g_free(s);
+ }
+
+ return db;
+}
+
+static gchar *create_class_name(const gchar *rname)
+{
+ gchar *rclass = g_strdup(rname);
+ gchar *p = rclass;
+
+ while (TRUE) {
+ *p = toupper(*p);
+ p = strchr(p+1, '.');
+ if (p == NULL) break;
+ ++p;
+ if (*p == '\0') break;
+ }
+ return rclass;
+}
+
+static gboolean read_int(XrmDatabase db, const gchar *rname, gint *value)
+{
+ gboolean ret = FALSE;
+ gchar *rclass = create_class_name(rname);
+ gchar *rettype, *end;
+ XrmValue retvalue;
+
+ if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
+ retvalue.addr != NULL) {
+ *value = (gint)strtol(retvalue.addr, &end, 10);
+ if (end != retvalue.addr)
+ ret = TRUE;
+ }
+
+ g_free(rclass);
+ return ret;
+}
+
+static gboolean read_string(XrmDatabase db, const gchar *rname, gchar **value)
+{
+ gboolean ret = FALSE;
+ gchar *rclass = create_class_name(rname);
+ gchar *rettype;
+ XrmValue retvalue;
+
+ if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
+ retvalue.addr != NULL) {
+ g_strstrip(retvalue.addr);
+ *value = retvalue.addr;
+ ret = TRUE;
+ }
+
+ g_free(rclass);
+ return ret;
+}
+
+static gboolean read_color(XrmDatabase db, const RrInstance *inst,
+ const gchar *rname, RrColor **value)
+{
+ gboolean ret = FALSE;
+ gchar *rclass = create_class_name(rname);
+ gchar *rettype;
+ XrmValue retvalue;
+
+ if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
+ retvalue.addr != NULL) {
+ RrColor *c;
+
+ /* retvalue.addr is inside the xrdb database so we can't destroy it
+ but we can edit it in place, as g_strstrip does. */
+ g_strstrip(retvalue.addr);
+ c = RrColorParse(inst, retvalue.addr);
+ if (c != NULL) {
+ *value = c;
+ ret = TRUE;
+ }
+ }
+
+ g_free(rclass);
+ return ret;
+}
+
+static gboolean read_mask(const RrInstance *inst, const gchar *path,
+ RrTheme *theme, const gchar *maskname,
+ RrPixmapMask **value)
+{
+ gboolean ret = FALSE;
+ gchar *s;
+ gint hx, hy; /* ignored */
+ guint w, h;
+ guchar *b;
+
+ s = g_build_filename(path, maskname, NULL);
+ if (XReadBitmapFileData(s, &w, &h, &b, &hx, &hy) == BitmapSuccess) {
+ ret = TRUE;
+ *value = RrPixmapMaskNew(inst, w, h, (gchar*)b);
+ XFree(b);
+ }
+ g_free(s);
+
+ return ret;
+}
+
+static void parse_appearance(gchar *tex, RrSurfaceColorType *grad,
+ RrReliefType *relief, RrBevelType *bevel,
+ gboolean *interlaced, gboolean *border,
+ gboolean allow_trans)
+{
+ gchar *t;
+
+ /* convert to all lowercase */
+ for (t = tex; *t != '\0'; ++t)
+ *t = g_ascii_tolower(*t);
+
+ if (allow_trans && strstr(tex, "parentrelative") != NULL) {
+ *grad = RR_SURFACE_PARENTREL;
+ } else {
+ if (strstr(tex, "gradient") != NULL) {
+ if (strstr(tex, "crossdiagonal") != NULL)
+ *grad = RR_SURFACE_CROSS_DIAGONAL;
+ else if (strstr(tex, "pyramid") != NULL)
+ *grad = RR_SURFACE_PYRAMID;
+ else if (strstr(tex, "mirrorhorizontal") != NULL)
+ *grad = RR_SURFACE_MIRROR_HORIZONTAL;
+ else if (strstr(tex, "horizontal") != NULL)
+ *grad = RR_SURFACE_HORIZONTAL;
+ else if (strstr(tex, "splitvertical") != NULL)
+ *grad = RR_SURFACE_SPLIT_VERTICAL;
+ else if (strstr(tex, "vertical") != NULL)
+ *grad = RR_SURFACE_VERTICAL;
+ else
+ *grad = RR_SURFACE_DIAGONAL;
+ } else {
+ *grad = RR_SURFACE_SOLID;
+ }
+ }
+
+ if (strstr(tex, "sunken") != NULL)
+ *relief = RR_RELIEF_SUNKEN;
+ else if (strstr(tex, "flat") != NULL)
+ *relief = RR_RELIEF_FLAT;
+ else if (strstr(tex, "raised") != NULL)
+ *relief = RR_RELIEF_RAISED;
+ else
+ *relief = (*grad == RR_SURFACE_PARENTREL) ?
+ RR_RELIEF_FLAT : RR_RELIEF_RAISED;
+
+ *border = FALSE;
+ if (*relief == RR_RELIEF_FLAT) {
+ if (strstr(tex, "border") != NULL)
+ *border = TRUE;
+ } else {
+ if (strstr(tex, "bevel2") != NULL)
+ *bevel = RR_BEVEL_2;
+ else
+ *bevel = RR_BEVEL_1;
+ }
+
+ if (strstr(tex, "interlaced") != NULL)
+ *interlaced = TRUE;
+ else
+ *interlaced = FALSE;
+}
+
+static gboolean read_appearance(XrmDatabase db, const RrInstance *inst,
+ const gchar *rname, RrAppearance *value,
+ gboolean allow_trans)
+{
+ gboolean ret = FALSE;
+ gchar *rclass = create_class_name(rname);
+ gchar *cname, *ctoname, *bcname, *icname, *hname, *sname;
+ gchar *csplitname, *ctosplitname;
+ gchar *rettype;
+ XrmValue retvalue;
+ gint i;
+
+ cname = g_strconcat(rname, ".color", NULL);
+ ctoname = g_strconcat(rname, ".colorTo", NULL);
+ bcname = g_strconcat(rname, ".border.color", NULL);
+ icname = g_strconcat(rname, ".interlace.color", NULL);
+ hname = g_strconcat(rname, ".highlight", NULL);
+ sname = g_strconcat(rname, ".shadow", NULL);
+ csplitname = g_strconcat(rname, ".color.splitTo", NULL);
+ ctosplitname = g_strconcat(rname, ".colorTo.splitTo", NULL);
+
+ if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
+ retvalue.addr != NULL) {
+ parse_appearance(retvalue.addr,
+ &value->surface.grad,
+ &value->surface.relief,
+ &value->surface.bevel,
+ &value->surface.interlaced,
+ &value->surface.border,
+ allow_trans);
+ if (!read_color(db, inst, cname, &value->surface.primary))
+ value->surface.primary = RrColorNew(inst, 0, 0, 0);
+ if (!read_color(db, inst, ctoname, &value->surface.secondary))
+ value->surface.secondary = RrColorNew(inst, 0, 0, 0);
+ if (value->surface.border)
+ if (!read_color(db, inst, bcname,
+ &value->surface.border_color))
+ value->surface.border_color = RrColorNew(inst, 0, 0, 0);
+ if (value->surface.interlaced)
+ if (!read_color(db, inst, icname,
+ &value->surface.interlace_color))
+ value->surface.interlace_color = RrColorNew(inst, 0, 0, 0);
+ if (read_int(db, hname, &i) && i >= 0)
+ value->surface.bevel_light_adjust = i;
+ if (read_int(db, sname, &i) && i >= 0 && i <= 256)
+ value->surface.bevel_dark_adjust = i;
+
+ if (value->surface.grad == RR_SURFACE_SPLIT_VERTICAL) {
+ gint r, g, b;
+
+ if (!read_color(db, inst, csplitname,
+ &value->surface.split_primary))
+ {
+ r = value->surface.primary->r;
+ r += r >> 2;
+ g = value->surface.primary->g;
+ g += g >> 2;
+ b = value->surface.primary->b;
+ b += b >> 2;
+ if (r > 0xFF) r = 0xFF;
+ if (g > 0xFF) g = 0xFF;
+ if (b > 0xFF) b = 0xFF;
+ value->surface.split_primary = RrColorNew(inst, r, g, b);
+ }
+
+ if (!read_color(db, inst, ctosplitname,
+ &value->surface.split_secondary))
+ {
+ r = value->surface.secondary->r;
+ r += r >> 4;
+ g = value->surface.secondary->g;
+ g += g >> 4;
+ b = value->surface.secondary->b;
+ b += b >> 4;
+ if (r > 0xFF) r = 0xFF;
+ if (g > 0xFF) g = 0xFF;
+ if (b > 0xFF) b = 0xFF;
+ value->surface.split_secondary = RrColorNew(inst, r, g, b);
+ }
+ }
+
+ ret = TRUE;
+ }
+
+ g_free(ctosplitname);
+ g_free(csplitname);
+ g_free(sname);
+ g_free(hname);
+ g_free(icname);
+ g_free(bcname);
+ g_free(ctoname);
+ g_free(cname);
+ g_free(rclass);
+ return ret;
+}
+
+static int parse_inline_number(const char *p)
+{
+ int neg = 1;
+ int res = 0;
+ if (*p == '-') {
+ neg = -1;
+ ++p;
+ }
+ for (; isdigit(*p); ++p)
+ res = res * 10 + *p - '0';
+ res *= neg;
+ return res;
+}
+
+static void set_default_appearance(RrAppearance *a)
+{
+ a->surface.grad = RR_SURFACE_SOLID;
+ a->surface.relief = RR_RELIEF_FLAT;
+ a->surface.bevel = RR_BEVEL_1;
+ a->surface.interlaced = FALSE;
+ a->surface.border = FALSE;
+ a->surface.primary = RrColorNew(a->inst, 0, 0, 0);
+ a->surface.secondary = RrColorNew(a->inst, 0, 0, 0);
+}
+
+/* Reads the output from gimp's C-Source file format into valid RGBA data for
+ an RrTextureRGBA. */
+static RrPixel32* read_c_image(gint width, gint height, const guint8 *data)
+{
+ RrPixel32 *im, *p;
+ gint i;
+
+ p = im = g_memdup(data, width * height * sizeof(RrPixel32));
+
+ for (i = 0; i < width * height; ++i) {
+ guchar a = ((*p >> 24) & 0xff);
+ guchar b = ((*p >> 16) & 0xff);
+ guchar g = ((*p >> 8) & 0xff);
+ guchar r = ((*p >> 0) & 0xff);
+
+ *p = ((r << RrDefaultRedOffset) +
+ (g << RrDefaultGreenOffset) +
+ (b << RrDefaultBlueOffset) +
+ (a << RrDefaultAlphaOffset));
+ p++;
+ }
+
+ return im;
+}
+
+static void read_button_colors(XrmDatabase db, const RrInstance *inst,
+ const RrTheme *theme, RrButton *btn,
+ const gchar *btnname)
+{
+ gchar *name;
+
+ /* active unpressed */
+ name = g_strdup_printf("window.active.button.%s.unpressed.image.color",
+ btnname);
+ READ_COLOR(name, btn->focused_unpressed_color,
+ RrColorCopy(theme->titlebut_focused_unpressed_color));
+ g_free(name);
+
+ /* inactive unpressed */
+ name = g_strdup_printf("window.inactive.button.%s.unpressed.image.color",
+ btnname);
+ READ_COLOR(name, btn->unfocused_unpressed_color,
+ RrColorCopy(theme->titlebut_unfocused_unpressed_color));
+ g_free(name);
+
+ /* active pressed */
+ name = g_strdup_printf("window.active.button.%s.pressed.image.color",
+ btnname);
+ READ_COLOR(name, btn->focused_pressed_color,
+ RrColorCopy(theme->titlebut_focused_pressed_color));
+ g_free(name);
+
+ /* inactive pressed */
+ name = g_strdup_printf("window.inactive.button.%s.pressed.image.color",
+ btnname);
+ READ_COLOR(name, btn->unfocused_pressed_color,
+ RrColorCopy(theme->titlebut_unfocused_pressed_color));
+ g_free(name);
+
+ /* active disabled */
+ name = g_strdup_printf("window.active.button.%s.disabled.image.color",
+ btnname);
+ READ_COLOR(name, btn->disabled_focused_color,
+ RrColorCopy(theme->titlebut_disabled_focused_color));
+ g_free(name);
+
+ /* inactive disabled */
+ name = g_strdup_printf("window.inactive.button.%s.disabled.image.color",
+ btnname);
+ READ_COLOR(name, btn->disabled_unfocused_color,
+ RrColorCopy(theme->titlebut_disabled_unfocused_color));
+ g_free(name);
+
+ /* active hover */
+ name = g_strdup_printf("window.active.button.%s.hover.image.color",
+ btnname);
+ READ_COLOR(name, btn->hover_focused_color,
+ RrColorCopy(theme->titlebut_hover_focused_color));
+ g_free(name);
+
+ /* inactive hover */
+ name = g_strdup_printf("window.inactive.button.%s.hover.image.color",
+ btnname);
+ READ_COLOR(name, btn->hover_unfocused_color,
+ RrColorCopy(theme->titlebut_hover_unfocused_color));
+ g_free(name);
+
+ /* active toggled unpressed */
+ name = g_strdup_printf("window.active.button.%s.toggled."
+ "unpressed.image.color", btnname);
+ READ_COLOR(name, btn->toggled_focused_unpressed_color,
+ RrColorCopy(theme->titlebut_toggled_focused_unpressed_color));
+ g_free(name);
+
+ /* inactive toggled unpressed */
+ name = g_strdup_printf("window.inactive.button.%s.toggled."
+ "unpressed.image.color", btnname);
+ READ_COLOR(name, btn->toggled_unfocused_unpressed_color,
+ RrColorCopy(theme->titlebut_toggled_unfocused_unpressed_color));
+ g_free(name);
+
+ /* active toggled hover */
+ name = g_strdup_printf("window.active.button.%s.toggled.hover.image.color",
+ btnname);
+ READ_COLOR(name, btn->toggled_hover_focused_color,
+ RrColorCopy(theme->titlebut_toggled_hover_focused_color));
+
+ g_free(name);
+
+ /* inactive toggled hover */
+ name = g_strdup_printf("window.inactive.button.%s.toggled.hover."
+ "image.color", btnname);
+ READ_COLOR(name, btn->toggled_hover_unfocused_color,
+ RrColorCopy(theme->titlebut_toggled_hover_unfocused_color));
+ g_free(name);
+
+ /* active toggled pressed */
+ name = g_strdup_printf("window.active.button.%s.toggled.pressed."
+ "image.color", btnname);
+ READ_COLOR(name, btn->toggled_focused_pressed_color,
+ RrColorCopy(theme->titlebut_toggled_focused_pressed_color));
+ g_free(name);
+
+ /* inactive toggled pressed */
+ name = g_strdup_printf("window.inactive.button.%s.toggled.pressed."
+ "image.color", btnname);
+ READ_COLOR(name, btn->toggled_unfocused_pressed_color,
+ RrColorCopy(theme->titlebut_toggled_unfocused_pressed_color));
+ g_free(name);
+}
+
+
diff --git a/obrender/theme.h b/obrender/theme.h
new file mode 100644
index 0000000..eb9ac3f
--- /dev/null
+++ b/obrender/theme.h
@@ -0,0 +1,198 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ theme.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __theme_h
+#define __theme_h
+
+#include "render.h"
+
+G_BEGIN_DECLS
+
+typedef struct _RrTheme RrTheme;
+
+struct _RrTheme {
+ const RrInstance *inst;
+
+ /* style settings - fonts */
+ RrFont *win_font_focused;
+ RrFont *win_font_unfocused;
+ RrFont *menu_title_font;
+ RrFont *menu_font;
+ RrFont *osd_font_hilite;
+ RrFont *osd_font_unhilite;
+
+ /* style settings - geometry */
+ gint paddingx;
+ gint paddingy;
+ gint handle_height;
+ gint fbwidth; /*!< frame border width */
+ gint mbwidth; /*!< menu border width */
+ gint obwidth; /*!< osd border width */
+ gint ubwidth; /*!< undecorated frame border width */
+ gint cbwidthx;
+ gint cbwidthy;
+ gint menu_overlap_x;
+ gint menu_overlap_y;
+ gint menu_sep_width;
+ gint menu_sep_paddingx;
+ gint menu_sep_paddingy;
+ /* these ones are calculated, not set directly by the theme file */
+ gint win_font_height;
+ gint menu_title_font_height;
+ gint menu_font_height;
+ gint label_height;
+ gint title_height;
+ gint button_size;
+ gint grip_width;
+ gint menu_title_label_height;
+ gint menu_title_height;
+
+ /* style settings - colors */
+ RrColor *menu_border_color;
+ RrColor *osd_border_color;
+ RrColor *frame_focused_border_color;
+ RrColor *frame_undecorated_focused_border_color;
+ RrColor *frame_unfocused_border_color;
+ RrColor *frame_undecorated_unfocused_border_color;
+ RrColor *title_separator_focused_color;
+ RrColor *title_separator_unfocused_color;
+ RrColor *cb_focused_color;
+ RrColor *cb_unfocused_color;
+ RrColor *title_focused_color;
+ RrColor *title_unfocused_color;
+ RrColor *titlebut_disabled_focused_color;
+ RrColor *titlebut_disabled_unfocused_color;
+ RrColor *titlebut_hover_focused_color;
+ RrColor *titlebut_hover_unfocused_color;
+ RrColor *titlebut_toggled_hover_focused_color;
+ RrColor *titlebut_toggled_hover_unfocused_color;
+ RrColor *titlebut_toggled_focused_pressed_color;
+ RrColor *titlebut_toggled_unfocused_pressed_color;
+ RrColor *titlebut_toggled_focused_unpressed_color;
+ RrColor *titlebut_toggled_unfocused_unpressed_color;
+ RrColor *titlebut_focused_pressed_color;
+ RrColor *titlebut_unfocused_pressed_color;
+ RrColor *titlebut_focused_unpressed_color;
+ RrColor *titlebut_unfocused_unpressed_color;
+ RrColor *menu_title_color;
+ RrColor *menu_sep_color;
+ RrColor *menu_color;
+ RrColor *menu_bullet_color;
+ RrColor *menu_bullet_selected_color;
+ RrColor *menu_selected_color;
+ RrColor *menu_disabled_color;
+ RrColor *menu_disabled_selected_color;
+ RrColor *title_focused_shadow_color;
+ gchar title_focused_shadow_alpha;
+ RrColor *title_unfocused_shadow_color;
+ gchar title_unfocused_shadow_alpha;
+ RrColor *osd_text_active_color;
+ RrColor *osd_text_inactive_color;
+ RrColor *osd_text_active_shadow_color;
+ RrColor *osd_text_inactive_shadow_color;
+ gchar osd_text_active_shadow_alpha;
+ gchar osd_text_inactive_shadow_alpha;
+ RrColor *osd_pressed_color;
+ RrColor *osd_unpressed_color;
+ RrColor *osd_focused_color;
+ RrColor *osd_pressed_lineart;
+ RrColor *osd_focused_lineart;
+ RrColor *menu_title_shadow_color;
+ gchar menu_title_shadow_alpha;
+ RrColor *menu_text_normal_shadow_color;
+ gchar menu_text_normal_shadow_alpha;
+ RrColor *menu_text_selected_shadow_color;
+ gchar menu_text_selected_shadow_alpha;
+ RrColor *menu_text_disabled_shadow_color;
+ gchar menu_text_disabled_shadow_alpha;
+ RrColor *menu_text_disabled_selected_shadow_color;
+ gchar menu_text_disabled_selected_shadow_alpha;
+
+ /* style settings - pics */
+ RrPixel32 *def_win_icon; /* RGBA */
+ gint def_win_icon_w;
+ gint def_win_icon_h;
+
+ /* style settings - masks */
+ RrPixmapMask *menu_bullet_mask; /* submenu pointer */
+#if 0
+ RrPixmapMask *menu_toggle_mask; /* menu boolean */
+#endif
+
+ RrPixmapMask *down_arrow_mask;
+ RrPixmapMask *up_arrow_mask;
+
+ /* buttons */
+ RrButton *btn_max;
+ RrButton *btn_close;
+ RrButton *btn_desk;
+ RrButton *btn_shade;
+ RrButton *btn_iconify;
+
+ /* global appearances */
+ RrAppearance *a_focused_grip;
+ RrAppearance *a_unfocused_grip;
+ RrAppearance *a_focused_title;
+ RrAppearance *a_unfocused_title;
+ RrAppearance *a_focused_label;
+ RrAppearance *a_unfocused_label;
+ /* always parentrelative, so no focused/unfocused */
+ RrAppearance *a_icon;
+ RrAppearance *a_focused_handle;
+ RrAppearance *a_unfocused_handle;
+ RrAppearance *a_menu_text_title;
+ RrAppearance *a_menu_title;
+ RrAppearance *a_menu;
+ RrAppearance *a_menu_normal;
+ RrAppearance *a_menu_selected;
+ RrAppearance *a_menu_disabled;
+ RrAppearance *a_menu_disabled_selected;
+ RrAppearance *a_menu_text_normal;
+ RrAppearance *a_menu_text_disabled;
+ RrAppearance *a_menu_text_disabled_selected;
+ RrAppearance *a_menu_text_selected;
+ RrAppearance *a_menu_bullet_normal;
+ RrAppearance *a_menu_bullet_selected;
+ RrAppearance *a_clear; /* clear with no texture */
+ RrAppearance *a_clear_tex; /* clear with a texture */
+
+ RrAppearance *osd_bg; /* can never be parent relative */
+ RrAppearance *osd_hilite_bg; /* can never be parent relative */
+ RrAppearance *osd_hilite_label; /* can be parent relative */
+ RrAppearance *osd_unhilite_bg; /* can never be parent relative */
+ RrAppearance *osd_unhilite_label; /* can be parent relative */
+ RrAppearance *osd_pressed_button;
+ RrAppearance *osd_unpressed_button;
+ RrAppearance *osd_focused_button;
+
+ gchar *name;
+};
+
+/*! The font values are all optional. If a NULL is used for any of them, then
+ the default font will be used. */
+RrTheme* RrThemeNew(const RrInstance *inst, const gchar *theme,
+ gboolean allow_fallback,
+ RrFont *active_window_font, RrFont *inactive_window_font,
+ RrFont *menu_title_font, RrFont *menu_item_font,
+ RrFont *active_osd_font, RrFont *inactive_osd_font);
+void RrThemeFree(RrTheme *theme);
+
+G_END_DECLS
+
+#endif
diff --git a/obrender/version.h.in b/obrender/version.h.in
new file mode 100644
index 0000000..0ff30b5
--- /dev/null
+++ b/obrender/version.h.in
@@ -0,0 +1,15 @@
+#ifndef rr__version_h
+#define rr__version_h
+
+#define RR_MAJOR_VERSION @RR_MAJOR_VERSION@
+#define RR_MINOR_VERSION @RR_MINOR_VERSION@
+#define RR_MICRO_VERSION @RR_MICRO_VERSION@
+#define RR_VERSION RR_MAJOR_VERSION.RR_MINOR_VERSION.RR_MICRO_VERSION
+
+#define RR_CHECK_VERSION(major,minor,micro) \
+ (RR_MAJOR_VERSION > (major) || \
+ (RR_MAJOR_VERSION == (major) && RR_MINOR_VERSION > (minor)) || \
+ (RR_MAJOR_VERSION == (major) && RR_MINOR_VERSION == (minor) && \
+ RR_MICRO_VERSION >= (micro)))
+
+#endif
diff --git a/obt/Makefile b/obt/Makefile
new file mode 100644
index 0000000..b90edac
--- /dev/null
+++ b/obt/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/obt/bsearch.h b/obt/bsearch.h
new file mode 100644
index 0000000..9613c51
--- /dev/null
+++ b/obt/bsearch.h
@@ -0,0 +1,61 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/bsearch.h for the Openbox window manager
+ Copyright (c) 2010 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __obt_bsearch_h
+#define __obt_bsearch_h
+
+#include <glib.h>
+
+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;
+
+/*! 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) \
+{ \
+ l_BSEARCH = (start); \
+ r_BSEARCH = (start)+(size)-1; \
+ while (l_BSEARCH <= r_BSEARCH) { \
+ /* 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]) { \
+ break; \
+ } \
+ else if ((val) < (ar)[out_BSEARCH] && out_BSEARCH > 0) { \
+ r_BSEARCH = out_BSEARCH-1; /* search to the left side */ \
+ } \
+ else \
+ l_BSEARCH = out_BSEARCH+1; /* search to the left side */ \
+ } \
+}
+
+/*! Returns true if the element last searched for was found in the array */
+#define BSEARCH_FOUND() (l_BSEARCH <= r_BSEARCH)
+/*! Returns the position in the array at which the element last searched for
+ was found. */
+#define BSEARCH_AT() (out_BSEARCH)
+
+
+
+G_END_DECLS
+
+#endif
diff --git a/obt/ddparse.c b/obt/ddparse.c
new file mode 100644
index 0000000..149134d
--- /dev/null
+++ b/obt/ddparse.c
@@ -0,0 +1,816 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/ddparse.c for the Openbox window manager
+ Copyright (c) 2009 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "obt/ddparse.h"
+#include "obt/link.h"
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+typedef struct _ObtDDParse ObtDDParse;
+
+/* Parses the value and adds it to the group's key_hash, with the given
+ key
+ Return TRUE if it is added to the hash table, and FALSE if not.
+*/
+typedef gboolean (*ObtDDParseValueFunc)(gchar *key, const gchar *val,
+ ObtDDParse *parse, gboolean *error);
+
+
+enum {
+ DE_TYPE = 1 << 0,
+ DE_TYPE_APPLICATION = 1 << 1,
+ DE_TYPE_LINK = 1 << 2,
+ DE_NAME = 1 << 3,
+ DE_EXEC = 1 << 4,
+ DE_URL = 1 << 5
+};
+
+struct _ObtDDParse {
+ gchar *filename;
+ gulong lineno;
+ gulong flags;
+ ObtDDParseGroup *group;
+ /* the key is a group name, the value is a ObtDDParseGroup */
+ GHashTable *group_hash;
+};
+
+struct _ObtDDParseGroup {
+ gchar *name;
+ gboolean seen;
+ ObtDDParseValueFunc value_func;
+ /* the key is a string (a key inside the group in the .desktop).
+ the value is an ObtDDParseValue */
+ GHashTable *key_hash;
+};
+
+/* Displays a warning message including the file name and line number, and
+ sets the boolean @error to true if it points to a non-NULL address.
+*/
+static void parse_error(const gchar *m, const ObtDDParse *const parse,
+ gboolean *error)
+{
+ if (!parse->filename)
+ g_warning("%s at line %lu of input", m, parse->lineno);
+ else
+ g_warning("%s at line %lu of file %s",
+ m, parse->lineno, parse->filename);
+ if (error) *error = TRUE;
+}
+
+static void parse_value_free(ObtDDParseValue *v)
+{
+ switch (v->type) {
+ case OBT_DDPARSE_EXEC:
+ case OBT_DDPARSE_STRING:
+ case OBT_DDPARSE_LOCALESTRING:
+ g_free(v->value.string); break;
+ case OBT_DDPARSE_STRINGS:
+ case OBT_DDPARSE_LOCALESTRINGS:
+ g_strfreev(v->value.strings.a);
+ v->value.strings.n = 0;
+ break;
+ case OBT_DDPARSE_BOOLEAN:
+ case OBT_DDPARSE_NUMERIC:
+ case OBT_DDPARSE_ENUM_TYPE:
+ case OBT_DDPARSE_ENVIRONMENTS:
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ g_slice_free(ObtDDParseValue, v);
+}
+
+static ObtDDParseGroup* parse_group_new(gchar *name, ObtDDParseValueFunc f)
+{
+ ObtDDParseGroup *g = g_slice_new(ObtDDParseGroup);
+ g->name = name;
+ g->value_func = f;
+ g->seen = FALSE;
+ g->key_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify)parse_value_free);
+ return g;
+}
+
+static void parse_group_free(ObtDDParseGroup *g)
+{
+ g_free(g->name);
+ g_hash_table_destroy(g->key_hash);
+ g_slice_free(ObtDDParseGroup, g);
+}
+
+/*! Reads an input string, strips out invalid stuff, and parses
+ backslash-stuff.
+ */
+static gchar* parse_value_string(const gchar *in,
+ gboolean locale,
+ gboolean semicolonterminate,
+ gulong *len,
+ const ObtDDParse *const parse,
+ gboolean *error)
+{
+ gint bytes;
+ gboolean backslash;
+ gchar *out, *o;
+ const gchar *end, *i;
+
+ /* find the end/size of the string */
+ backslash = FALSE;
+ for (end = in; *end; ++end) {
+ if (semicolonterminate) {
+ if (backslash) backslash = FALSE;
+ else if (*end == '\\') backslash = TRUE;
+ else if (*end == ';') break;
+ }
+ }
+ bytes = end - in;
+
+ g_return_val_if_fail(in != NULL, NULL);
+
+ if (locale && !g_utf8_validate(in, bytes, &end)) {
+ parse_error("Invalid bytes in localestring", parse, error);
+ bytes = end - in;
+ }
+
+ out = g_new(char, bytes + 1);
+ if (len) *len = 0;
+ i = in; o = out;
+ backslash = FALSE;
+ while (i < end) {
+ const gchar *next;
+
+ /* find the next character in the string */
+ if (!locale) next = i+1;
+ else if (!(next = g_utf8_find_next_char(i, end))) next = end;
+
+ if (backslash) {
+ switch(*i) {
+ case 's': *o++ = ' '; break;
+ case 'n': *o++ = '\n'; break;
+ case 't': *o++ = '\t'; break;
+ case 'r': *o++ = '\r'; break;
+ case ';': *o++ = ';'; break;
+ case '\\': *o++ = '\\'; break;
+ default:
+ parse_error((locale ?
+ "Invalid escape sequence in localestring" :
+ "Invalid escape sequence in string"),
+ parse, error);
+ }
+ backslash = FALSE;
+ }
+ else if (*i == '\\')
+ backslash = TRUE;
+ else if ((guchar)*i >= 127 || (guchar)*i < 32) {
+ /* avoid ascii control characters */
+ parse_error("Found control character in string", parse, error);
+ break;
+ }
+ else {
+ const gulong s = next-i;
+ memcpy(o, i, s);
+ o += s;
+ if (len) *len += s;
+ }
+ i = next;
+ }
+ *o = '\0';
+ return out;
+}
+
+
+/*! Reads a list of input strings, strips out invalid stuff, and parses
+ backslash-stuff.
+ */
+static gchar** parse_value_strings(const gchar *in,
+ gboolean locale,
+ gulong *nstrings,
+ const ObtDDParse *const parse,
+ gboolean *error)
+{
+ gchar **out;
+ const gchar *i;
+
+ out = g_new(gchar*, 1);
+ out[0] = NULL;
+ *nstrings = 0;
+
+ i = in;
+ while (TRUE) {
+ gchar *a;
+ gulong len;
+
+ a = parse_value_string(i, locale, TRUE, &len, parse, error);
+ i += len;
+
+ if (len) {
+ (*nstrings)++;
+ out = g_renew(gchar*, out, *nstrings+1);
+ out[*nstrings-1] = a;
+ out[*nstrings] = NULL;
+ }
+
+ if (!*i) break; /* no more strings */
+ ++i;
+ }
+ return out;
+}
+
+static guint parse_value_environments(const gchar *in,
+ const ObtDDParse *const parse,
+ gboolean *error)
+{
+ const gchar *s;
+ guint mask = 0;
+
+ s = in;
+ while (*s) {
+ switch (*(s++)) {
+ case 'G':
+ if (strcmp(s, "NOME") == 0) {
+ mask |= OBT_LINK_ENV_GNOME;
+ s += 4;
+ }
+ break;
+ case 'K':
+ if (strcmp(s, "DE") == 0) {
+ mask |= OBT_LINK_ENV_KDE;
+ s += 2;
+ }
+ break;
+ case 'L':
+ if (strcmp(s, "XDE") == 0) {
+ mask |= OBT_LINK_ENV_LXDE;
+ s += 3;
+ }
+ break;
+ case 'R':
+ if (strcmp(s, "OX") == 0) {
+ mask |= OBT_LINK_ENV_ROX;
+ s += 2;
+ }
+ break;
+ case 'X':
+ if (strcmp(s, "FCE") == 0) {
+ mask |= OBT_LINK_ENV_XFCE;
+ s += 3;
+ }
+ break;
+ case 'O':
+ switch (*(s++)) {
+ case 'l':
+ if (strcmp(s, "d") == 0) {
+ mask |= OBT_LINK_ENV_OLD;
+ s += 1;
+ }
+ break;
+ case 'P':
+ if (strcmp(s, "ENBOX") == 0) {
+ mask |= OBT_LINK_ENV_OPENBOX;
+ s += 5;
+ }
+ break;
+ }
+ }
+ /* find the next string, or the end of the sequence */
+ while (*s && *s != ';') ++s;
+ }
+ return mask;
+}
+
+static gboolean parse_value_boolean(const gchar *in,
+ const ObtDDParse *const parse,
+ gboolean *error)
+{
+ if (strcmp(in, "true") == 0)
+ return TRUE;
+ else if (strcmp(in, "false") != 0)
+ parse_error("Invalid boolean value", parse, error);
+ return FALSE;
+}
+
+static gfloat parse_value_numeric(const gchar *in,
+ const ObtDDParse *const parse,
+ gboolean *error)
+{
+ gfloat out = 0;
+ if (sscanf(in, "%f", &out) == 0)
+ parse_error("Invalid numeric value", parse, error);
+ return out;
+}
+
+static gboolean parse_file_line(FILE *f, gchar **buf,
+ gulong *size, gulong *read,
+ ObtDDParse *parse, gboolean *error)
+{
+ const gulong BUFMUL = 80;
+ size_t ret;
+ gulong i, null;
+
+ if (*size == 0) {
+ g_assert(*read == 0);
+ *size = BUFMUL;
+ *buf = g_new(char, *size);
+ }
+
+ /* remove everything up to a null zero already in the buffer and shift
+ the rest to the front */
+ null = *size;
+ for (i = 0; i < *read; ++i) {
+ if (null < *size)
+ (*buf)[i-null-1] = (*buf)[i];
+ else if ((*buf)[i] == '\0')
+ null = i;
+ }
+ if (null < *size)
+ *read -= null + 1;
+
+ /* is there already a newline in the buffer? */
+ for (i = 0; i < *read; ++i)
+ if ((*buf)[i] == '\n') {
+ /* turn it into a null zero and done */
+ (*buf)[i] = '\0';
+ return TRUE;
+ }
+
+ /* we need to read some more to find a newline */
+ while (TRUE) {
+ gulong eol;
+ gchar *newread;
+
+ newread = *buf + *read;
+ ret = fread(newread, sizeof(char), *size-*read, f);
+ if (ret < *size - *read && !feof(f)) {
+ parse_error("Error reading", parse, error);
+ return FALSE;
+ }
+ *read += ret;
+
+ /* strip out null zeros in the input and look for an endofline */
+ null = 0;
+ eol = *size;
+ for (i = newread-*buf; i < *read; ++i) {
+ if (null > 0)
+ (*buf)[i] = (*buf)[i+null];
+ if ((*buf)[i] == '\0') {
+ ++null;
+ --(*read);
+ --i; /* try again */
+ }
+ else if ((*buf)[i] == '\n' && eol == *size) {
+ eol = i;
+ /* turn it into a null zero */
+ (*buf)[i] = '\0';
+ }
+ }
+
+ if (eol != *size)
+ /* found an endofline, done */
+ break;
+ else if (feof(f) && *read < *size) {
+ /* found the endoffile, done (if there is space) */
+ if (*read > 0) {
+ /* stick a null zero on if there is test on the last line */
+ (*buf)[(*read)++] = '\0';
+ }
+ break;
+ }
+ else {
+ /* read more */
+ size += BUFMUL;
+ *buf = g_renew(char, *buf, *size);
+ }
+ }
+ return *read > 0;
+}
+
+static void parse_group(const gchar *buf, gulong len,
+ ObtDDParse *parse, gboolean *error)
+{
+ ObtDDParseGroup *g;
+ gchar *group;
+ gulong i;
+
+ /* get the group name */
+ group = g_strndup(buf+1, len-2);
+ for (i = 0; i < len-2; ++i)
+ if ((guchar)group[i] < 32 || (guchar)group[i] >= 127) {
+ /* valid ASCII only */
+ parse_error("Invalid character found", parse, NULL);
+ group[i] = '\0'; /* stopping before this character */
+ break;
+ }
+
+ /* make sure it's a new group */
+ g = g_hash_table_lookup(parse->group_hash, group);
+ if (g && g->seen) {
+ parse_error("Duplicate group found", parse, error);
+ g_free(group);
+ return;
+ }
+ /* if it's the first group, make sure it's named Desktop Entry */
+ else if (!parse->group && strcmp(group, "Desktop Entry") != 0)
+ {
+ parse_error("Incorrect group found, "
+ "expected [Desktop Entry]",
+ parse, error);
+ g_free(group);
+ return;
+ }
+ else {
+ if (!g) {
+ g = parse_group_new(group, NULL);
+ g_hash_table_insert(parse->group_hash, g->name, g);
+ }
+ else
+ g_free(group);
+
+ g->seen = TRUE;
+ parse->group = g;
+ g_print("Found group %s\n", g->name);
+ }
+}
+
+static void parse_key_value(const gchar *buf, gulong len,
+ ObtDDParse *parse, gboolean *error)
+{
+ gulong i, keyend, valstart, eq;
+ char *key;
+
+ /* find the end of the key */
+ for (i = 0; i < len; ++i)
+ if (!(((guchar)buf[i] >= 'A' && (guchar)buf[i] <= 'Z') ||
+ ((guchar)buf[i] >= 'a' && (guchar)buf[i] <= 'z') ||
+ ((guchar)buf[i] >= '0' && (guchar)buf[i] <= '9') ||
+ ((guchar)buf[i] == '-'))) {
+ /* not part of the key */
+ break;
+ }
+ keyend = i;
+
+ if (keyend < 1) {
+ parse_error("Empty key", parse, error);
+ return;
+ }
+ /* find the = character */
+ for (i = keyend; i < len; ++i) {
+ if (buf[i] == '=') {
+ eq = i;
+ break;
+ }
+ else if (buf[i] != ' ') {
+ parse_error("Invalid character in key name", parse, error);
+ return ;
+ }
+ }
+ if (i == len) {
+ parse_error("Key without value found", parse, error);
+ return;
+ }
+ /* find the start of the value */
+ for (i = eq+1; i < len; ++i)
+ if (buf[i] != ' ') {
+ valstart = i;
+ break;
+ }
+ if (i == len) {
+ parse_error("Empty value found", parse, error);
+ return;
+ }
+
+ key = g_strndup(buf, keyend);
+ if (g_hash_table_lookup(parse->group->key_hash, key)) {
+ parse_error("Duplicate key found", parse, error);
+ g_free(key);
+ return;
+ }
+ g_print("Found key/value %s=%s.\n", key, buf+valstart);
+ if (parse->group->value_func)
+ if (!parse->group->value_func(key, buf+valstart, parse, error)) {
+ parse_error("Unknown key", parse, error);
+ g_free(key);
+ }
+}
+
+static gboolean parse_file(FILE *f, ObtDDParse *parse)
+{
+ gchar *buf = NULL;
+ gulong bytes = 0, read = 0;
+ gboolean error = FALSE;
+
+ while (!error && parse_file_line(f, &buf, &bytes, &read, parse, &error)) {
+ gulong len = strlen(buf);
+ if (buf[0] == '#' || buf[0] == '\0')
+ ; /* ignore comment lines */
+ else if (buf[0] == '[' && buf[len-1] == ']')
+ parse_group(buf, len, parse, &error);
+ else if (!parse->group)
+ /* just ignore keys outside of groups */
+ parse_error("Key found before group", parse, NULL);
+ else
+ /* ignore errors in key-value pairs and continue */
+ parse_key_value(buf, len, parse, NULL);
+ ++parse->lineno;
+ }
+
+ if (buf) g_free(buf);
+ return !error;
+}
+
+static gboolean parse_desktop_entry_value(gchar *key, const gchar *val,
+ ObtDDParse *parse, gboolean *error)
+{
+ ObtDDParseValue v, *pv;
+
+ switch (key[0]) {
+ case 'C':
+ switch (key[1]) {
+ case 'a': /* Categories */
+ if (strcmp(key+2, "tegories")) return FALSE;
+ v.type = OBT_DDPARSE_STRINGS; break;
+ case 'o': /* Comment */
+ if (strcmp(key+2, "mment")) return FALSE;
+ v.type = OBT_DDPARSE_LOCALESTRING; break;
+ default:
+ return FALSE;
+ }
+ break;
+ case 'E': /* Exec */
+ if (strcmp(key+1, "xec")) return FALSE;
+ v.type = OBT_DDPARSE_EXEC; parse->flags |= DE_EXEC; break;
+ case 'G': /* GenericName */
+ if (strcmp(key+1, "enericName")) return FALSE;
+ v.type = OBT_DDPARSE_LOCALESTRING; break;
+ case 'I': /* Icon */
+ if (strcmp(key+1, "con")) return FALSE;
+ v.type = OBT_DDPARSE_LOCALESTRING; break;
+ case 'H': /* Hidden */
+ if (strcmp(key+1, "idden")) return FALSE;
+ v.type = OBT_DDPARSE_BOOLEAN; break;
+ case 'M': /* MimeType */
+ if (strcmp(key+1, "imeType")) return FALSE;
+ v.type = OBT_DDPARSE_STRINGS; break;
+ case 'N':
+ switch (key[1]) {
+ case 'a': /* Name */
+ if (strcmp(key+2, "me")) return FALSE;
+ v.type = OBT_DDPARSE_LOCALESTRING; parse->flags |= DE_NAME; break;
+ case 'o':
+ switch (key[2]) {
+ case 'D': /* NoDisplay */
+ if (strcmp(key+3, "isplay")) return FALSE;
+ v.type = OBT_DDPARSE_BOOLEAN; break;
+ case 't': /* NotShowIn */
+ if (strcmp(key+3, "ShowIn")) return FALSE;
+ v.type = OBT_DDPARSE_STRINGS; break;
+ default:
+ return FALSE;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ break;
+ case 'P': /* Path */
+ if (strcmp(key+1, "ath")) return FALSE;
+ v.type = OBT_DDPARSE_STRING; break;
+ case 'S': /* Path */
+ if (key[1] == 't' && key[2] == 'a' && key[3] == 'r' &&
+ key[4] == 't' && key[5] == 'u' && key[6] == 'p')
+ switch (key[7]) {
+ case 'N': /* StartupNotify */
+ if (strcmp(key+8, "otify")) return FALSE;
+ v.type = OBT_DDPARSE_BOOLEAN; break;
+ case 'W': /* StartupWMClass */
+ if (strcmp(key+8, "MClass")) return FALSE;
+ v.type = OBT_DDPARSE_STRING; break;
+ default:
+ return FALSE;
+ }
+ else
+ return FALSE;
+ break;
+ case 'T':
+ switch (key[1]) {
+ case 'e': /* Terminal */
+ if (strcmp(key+2, "rminal")) return FALSE;
+ v.type = OBT_DDPARSE_BOOLEAN; break;
+ case 'r': /* TryExec */
+ if (strcmp(key+2, "yExec")) return FALSE;
+ v.type = OBT_DDPARSE_STRING; break;
+ case 'y': /* Type */
+ if (strcmp(key+2, "pe")) return FALSE;
+ v.type = OBT_DDPARSE_ENUM_TYPE; parse->flags |= DE_TYPE; break;
+ default:
+ return FALSE;
+ }
+ break;
+ case 'U': /* URL */
+ if (strcmp(key+1, "RL")) return FALSE;
+ v.type = OBT_DDPARSE_STRING; parse->flags |= DE_URL; break;
+ case 'V': /* MimeType */
+ if (strcmp(key+1, "ersion")) return FALSE;
+ v.type = OBT_DDPARSE_STRING; break;
+ default:
+ return FALSE;
+ }
+
+ /* parse the value */
+ switch (v.type) {
+ case OBT_DDPARSE_EXEC: {
+ gchar *c, *m;
+ gboolean percent;
+ gboolean found;
+
+ v.value.string = parse_value_string(val, FALSE, FALSE, NULL,
+ parse, error);
+ g_assert(v.value.string);
+
+ /* an exec string can only contain one of the file/url-opening %'s */
+ percent = found = FALSE;
+ for (c = v.value.string; *c; ++c) {
+ if (percent) {
+ switch (*c) {
+ case 'f':
+ case 'F':
+ case 'u':
+ case 'U':
+ if (found) {
+ m = g_strdup_printf("Malformed Exec key, "
+ "extraneous %%%c", *c);
+ parse_error(m, parse, error);
+ g_free(m);
+ }
+ found = TRUE;
+ break;
+ case 'd':
+ case 'D':
+ case 'n':
+ case 'N':
+ case 'v':
+ case 'm':
+ m = g_strdup_printf("Malformed Exec key, "
+ "uses deprecated %%%c", *c);
+ parse_error(m, parse, NULL); /* just a warning */
+ g_free(m);
+ break;
+ case 'i':
+ case 'c':
+ case 'k':
+ case '%':
+ break;
+ default:
+ m = g_strdup_printf("Malformed Exec key, "
+ "uses unknown %%%c", *c);
+ parse_error(m, parse, NULL); /* just a warning */
+ g_free(m);
+ }
+ percent = FALSE;
+ }
+ else if (*c == '%') percent = TRUE;
+ }
+ break;
+ }
+ case OBT_DDPARSE_STRING:
+ v.value.string = parse_value_string(val, FALSE, FALSE, NULL,
+ parse, error);
+ g_assert(v.value.string);
+ break;
+ case OBT_DDPARSE_LOCALESTRING:
+ v.value.string = parse_value_string(val, TRUE, FALSE, NULL,
+ parse, error);
+ g_assert(v.value.string);
+ break;
+ case OBT_DDPARSE_STRINGS:
+ v.value.strings.a = parse_value_strings(val, FALSE, &v.value.strings.n,
+ parse, error);
+ g_assert(v.value.strings.a);
+ g_assert(v.value.strings.n);
+ break;
+ case OBT_DDPARSE_LOCALESTRINGS:
+ v.value.strings.a = parse_value_strings(val, TRUE, &v.value.strings.n,
+ parse, error);
+ g_assert(v.value.strings.a);
+ g_assert(v.value.strings.n);
+ break;
+ case OBT_DDPARSE_BOOLEAN:
+ v.value.boolean = parse_value_boolean(val, parse, error);
+ break;
+ case OBT_DDPARSE_NUMERIC:
+ v.value.numeric = parse_value_numeric(val, parse, error);
+ break;
+ case OBT_DDPARSE_ENUM_TYPE:
+ if (val[0] == 'A' && strcmp(val+1, "pplication") == 0) {
+ v.value.enumerable = OBT_LINK_TYPE_APPLICATION;
+ parse->flags |= DE_TYPE_APPLICATION;
+ }
+ else if (val[0] == 'L' && strcmp(val+1, "ink") == 0) {
+ v.value.enumerable = OBT_LINK_TYPE_URL;
+ parse->flags |= DE_TYPE_LINK;
+ }
+ else if (val[0] == 'D' && strcmp(val+1, "irectory") == 0)
+ v.value.enumerable = OBT_LINK_TYPE_DIRECTORY;
+ else {
+ parse_error("Unknown Type", parse, error);
+ return FALSE;
+ }
+ break;
+ case OBT_DDPARSE_ENVIRONMENTS:
+ v.value.environments = parse_value_environments(val, parse, error);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ pv = g_slice_new(ObtDDParseValue);
+ *pv = v;
+ g_hash_table_insert(parse->group->key_hash, key, pv);
+ return TRUE;
+}
+
+GHashTable* obt_ddparse_file(const gchar *name, GSList *paths)
+{
+ ObtDDParse parse;
+ ObtDDParseGroup *desktop_entry;
+ GSList *it;
+ FILE *f;
+ gboolean success;
+
+ parse.filename = NULL;
+ parse.lineno = 0;
+ parse.group = NULL;
+ parse.group_hash = g_hash_table_new_full(g_str_hash,
+ g_str_equal,
+ NULL,
+ (GDestroyNotify)parse_group_free);
+
+ /* set up the groups (there's only one right now) */
+ desktop_entry = parse_group_new(g_strdup("Desktop Entry"),
+ parse_desktop_entry_value);
+ g_hash_table_insert(parse.group_hash, desktop_entry->name, desktop_entry);
+
+ success = FALSE;
+ for (it = paths; it && !success; it = g_slist_next(it)) {
+ gchar *path = g_strdup_printf("%s/%s", (char*)it->data, name);
+ if ((f = fopen(path, "r"))) {
+ parse.filename = path;
+ parse.lineno = 1;
+ parse.flags = 0;
+ if ((success = parse_file(f, &parse))) {
+ /* check that required keys exist */
+
+ if (!(parse.flags & DE_TYPE)) {
+ g_warning("Missing Type key in %s", path);
+ success = FALSE;
+ }
+ if (!(parse.flags & DE_NAME)) {
+ g_warning("Missing Name key in %s", path);
+ success = FALSE;
+ }
+ if (parse.flags & DE_TYPE_APPLICATION &&
+ !(parse.flags & DE_EXEC))
+ {
+ g_warning("Missing Exec key for Application in %s",
+ path);
+ success = FALSE;
+ }
+ else if (parse.flags & DE_TYPE_LINK && !(parse.flags & DE_URL))
+ {
+ g_warning("Missing URL key for Link in %s", path);
+ success = FALSE;
+ }
+ }
+ fclose(f);
+ }
+ g_free(path);
+ }
+ if (!success) {
+ g_hash_table_destroy(parse.group_hash);
+ parse.group_hash = NULL;
+ }
+ return parse.group_hash;
+}
+
+GHashTable* obt_ddparse_group_keys(ObtDDParseGroup *g)
+{
+ return g->key_hash;
+}
diff --git a/obt/ddparse.h b/obt/ddparse.h
new file mode 100644
index 0000000..d261f5b
--- /dev/null
+++ b/obt/ddparse.h
@@ -0,0 +1,57 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/ddparse.h for the Openbox window manager
+ Copyright (c) 2009 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <glib.h>
+
+typedef struct _ObtDDParseGroup ObtDDParseGroup;
+
+typedef enum {
+ OBT_DDPARSE_EXEC,
+ OBT_DDPARSE_STRING,
+ OBT_DDPARSE_LOCALESTRING,
+ OBT_DDPARSE_STRINGS,
+ OBT_DDPARSE_LOCALESTRINGS,
+ OBT_DDPARSE_BOOLEAN,
+ OBT_DDPARSE_NUMERIC,
+ OBT_DDPARSE_ENUM_TYPE,
+ OBT_DDPARSE_ENVIRONMENTS,
+ OBT_DDPARSE_NUM_VALUE_TYPES
+} ObtDDParseValueType;
+
+typedef struct _ObtDDParseValue {
+ ObtDDParseValueType type;
+ union _ObtDDParseValueValue {
+ gchar *string;
+ struct _ObtDDParseValueStrings {
+ gchar **a;
+ gulong n;
+ } strings;
+ gboolean boolean;
+ gfloat numeric;
+ guint enumerable;
+ guint environments; /*!< A mask of flags from ObtLinkEnvMask */
+ } value;
+} ObtDDParseValue;
+
+/* Returns a hash table where the keys are groups, and the values are
+ ObtDDParseGroups */
+GHashTable* obt_ddparse_file(const gchar *name, GSList *paths);
+
+/* Returns a hash table where the keys are "keys" in the .desktop file,
+ and the values are "values" in the .desktop file, for the group @g. */
+GHashTable* obt_ddparse_group_keys(ObtDDParseGroup *g);
diff --git a/obt/display.c b/obt/display.c
new file mode 100644
index 0000000..8b06cbf
--- /dev/null
+++ b/obt/display.c
@@ -0,0 +1,165 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/display.c for the Openbox window manager
+ Copyright (c) 2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "obt/display.h"
+#include "obt/prop.h"
+#include "obt/internal.h"
+#include "obt/keyboard.h"
+#include "obt/xqueue.h"
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+/* from xqueue.c */
+extern void xqueue_init(void);
+extern void xqueue_destroy(void);
+
+Display* obt_display = NULL;
+
+gboolean obt_display_error_occured = FALSE;
+
+gboolean obt_display_extension_xkb = FALSE;
+gint obt_display_extension_xkb_basep;
+gboolean obt_display_extension_shape = FALSE;
+gint obt_display_extension_shape_basep;
+gboolean obt_display_extension_xinerama = FALSE;
+gint obt_display_extension_xinerama_basep;
+gboolean obt_display_extension_randr = FALSE;
+gint obt_display_extension_randr_basep;
+gboolean obt_display_extension_sync = FALSE;
+gint obt_display_extension_sync_basep;
+
+static gint xerror_handler(Display *d, XErrorEvent *e);
+
+static gboolean xerror_ignore = FALSE;
+
+gboolean obt_display_open(const char *display_name)
+{
+ gchar *n;
+ Display *d = NULL;
+
+ n = display_name ? g_strdup(display_name) : NULL;
+ obt_display = d = XOpenDisplay(n);
+ if (d) {
+ gint junk, major, minor;
+ (void)junk, (void)major, (void)minor;
+
+ if (fcntl(ConnectionNumber(d), F_SETFD, 1) == -1)
+ g_message("Failed to set display as close-on-exec");
+ XSetErrorHandler(xerror_handler);
+
+ /* read what extensions are present */
+#ifdef XKB
+ major = XkbMajorVersion;
+ minor = XkbMinorVersion;
+ obt_display_extension_xkb =
+ XkbQueryExtension(d, &junk,
+ &obt_display_extension_xkb_basep, &junk,
+ &major, &minor);
+ if (!obt_display_extension_xkb)
+ g_message("XKB extension is not present on the server or too old");
+#endif
+
+#ifdef SHAPE
+ obt_display_extension_shape =
+ XShapeQueryExtension(d, &obt_display_extension_shape_basep,
+ &junk);
+ if (!obt_display_extension_shape)
+ g_message("X Shape extension is not present on the server");
+#endif
+
+#ifdef XINERAMA
+ obt_display_extension_xinerama =
+ XineramaQueryExtension(d,
+ &obt_display_extension_xinerama_basep,
+ &junk) && XineramaIsActive(d);
+ if (!obt_display_extension_xinerama)
+ g_message("Xinerama extension is not present on the server");
+#endif
+
+#ifdef XRANDR
+ obt_display_extension_randr =
+ XRRQueryExtension(d, &obt_display_extension_randr_basep,
+ &junk);
+ if (!obt_display_extension_randr)
+ g_message("XRandR extension is not present on the server");
+#endif
+
+#ifdef SYNC
+ obt_display_extension_sync =
+ XSyncQueryExtension(d, &obt_display_extension_sync_basep,
+ &junk) && XSyncInitialize(d, &junk, &junk);
+ if (!obt_display_extension_sync)
+ g_message("X Sync extension is not present on the server or is an "
+ "incompatible version");
+#endif
+
+ obt_prop_startup();
+ obt_keyboard_reload();
+ }
+ g_free(n);
+
+ if (obt_display)
+ xqueue_init();
+
+ return obt_display != NULL;
+}
+
+void obt_display_close(void)
+{
+ obt_keyboard_shutdown();
+ if (obt_display) {
+ xqueue_destroy();
+ XCloseDisplay(obt_display);
+ }
+}
+
+static gint xerror_handler(Display *d, XErrorEvent *e)
+{
+#ifdef DEBUG
+ gchar errtxt[128];
+
+ XGetErrorText(d, e->error_code, errtxt, 127);
+ if (!xerror_ignore) {
+ if (e->error_code == BadWindow)
+ /*g_debug(_("X Error: %s\n"), errtxt)*/;
+ else
+ g_error("X Error: %s", errtxt);
+ } else
+ g_debug("Ignoring XError code %d '%s'", e->error_code, errtxt);
+#else
+ (void)d; (void)e;
+#endif
+
+ obt_display_error_occured = TRUE;
+ return 0;
+}
+
+void obt_display_ignore_errors(gboolean ignore)
+{
+ XSync(obt_display, FALSE);
+ xerror_ignore = ignore;
+ if (ignore) obt_display_error_occured = FALSE;
+}
diff --git a/obt/display.h b/obt/display.h
new file mode 100644
index 0000000..b5816a9
--- /dev/null
+++ b/obt/display.h
@@ -0,0 +1,70 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/display.h for the Openbox window manager
+ Copyright (c) 2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __obt_display_h
+#define __obt_display_h
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+#include <X11/Xutil.h> /* shape.h uses Region which is in here */
+#ifdef XKB
+#include <X11/XKBlib.h>
+#endif
+#ifdef SHAPE
+#include <X11/extensions/shape.h>
+#endif
+#ifdef XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif
+#ifdef XRANDR
+#include <X11/extensions/Xrandr.h>
+#endif
+#ifdef SYNC
+#include <X11/extensions/sync.h>
+#endif
+
+G_BEGIN_DECLS
+
+extern gboolean obt_display_error_occured;
+
+extern gboolean obt_display_extension_xkb;
+extern gint obt_display_extension_xkb_basep;
+extern gboolean obt_display_extension_shape;
+extern gint obt_display_extension_shape_basep;
+extern gboolean obt_display_extension_xinerama;
+extern gint obt_display_extension_xinerama_basep;
+extern gboolean obt_display_extension_randr;
+extern gint obt_display_extension_randr_basep;
+extern gboolean obt_display_extension_sync;
+extern gint obt_display_extension_sync_basep;
+
+extern Display* obt_display;
+
+/*! Open the X display. You should call g_set_prgname() before calling this
+ function for X Input Methods to work correctly. */
+gboolean obt_display_open(const char *display_name);
+void obt_display_close(void);
+
+void obt_display_ignore_errors(gboolean ignore);
+
+#define obt_root(screen) (RootWindow(obt_display, screen))
+
+G_END_DECLS
+
+#endif /*__obt_display_h*/
diff --git a/obt/internal.h b/obt/internal.h
new file mode 100644
index 0000000..818107d
--- /dev/null
+++ b/obt/internal.h
@@ -0,0 +1,27 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/internal.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __obt_internal_h
+#define __obt_internal_h
+
+void obt_prop_startup(void);
+
+void obt_keyboard_shutdown(void);
+
+#endif /* __obt_internal_h */
diff --git a/obt/keyboard.c b/obt/keyboard.c
new file mode 100644
index 0000000..ef2678b
--- /dev/null
+++ b/obt/keyboard.c
@@ -0,0 +1,445 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/keyboard.c for the Openbox window manager
+ Copyright (c) 2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "obt/display.h"
+#include "obt/keyboard.h"
+
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+
+struct _ObtIC
+{
+ guint ref;
+ XIC xic;
+ Window client;
+ Window focus;
+};
+
+/* These masks are constants and the modifier keys are bound to them as
+ anyone sees fit:
+ ShiftMask (1<<0), LockMask (1<<1), ControlMask (1<<2), Mod1Mask (1<<3),
+ Mod2Mask (1<<4), Mod3Mask (1<<5), Mod4Mask (1<<6), Mod5Mask (1<<7)
+*/
+#define NUM_MASKS 8
+#define ALL_MASKS 0xff /* an or'ing of all 8 keyboard masks */
+
+/* Get the bitflag for the n'th modifier mask */
+#define nth_mask(n) (1 << n)
+
+static void set_modkey_mask(guchar mask, KeySym sym);
+static void xim_init(void);
+void obt_keyboard_shutdown();
+void obt_keyboard_context_renew(ObtIC *ic);
+
+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 */
+static guchar modkeys_keys[OBT_KEYBOARD_NUM_MODKEYS];
+
+static gboolean alt_l = FALSE;
+static gboolean meta_l = FALSE;
+static gboolean super_l = FALSE;
+static gboolean hyper_l = FALSE;
+
+static gboolean started = FALSE;
+
+static XIM xim = NULL;
+static XIMStyle xim_style = 0;
+static GSList *xic_all = NULL;
+
+void obt_keyboard_reload(void)
+{
+ gint i, j, k;
+
+ if (started) obt_keyboard_shutdown(); /* free stuff */
+ started = TRUE;
+
+ xim_init();
+
+ /* reset the keys to not be bound to any masks */
+ for (i = 0; i < OBT_KEYBOARD_NUM_MODKEYS; ++i)
+ modkeys_keys[i] = 0;
+
+ modmap = XGetModifierMapping(obt_display);
+ /* note: modmap->max_keypermod can be 0 when there is no valid key layout
+ available */
+
+ XDisplayKeycodes(obt_display, &min_keycode, &max_keycode);
+ keymap = XGetKeyboardMapping(obt_display, min_keycode,
+ max_keycode - min_keycode + 1,
+ &keysyms_per_keycode);
+
+ alt_l = meta_l = super_l = hyper_l = FALSE;
+
+ /* go through each of the modifier masks (eg ShiftMask, CapsMask...) */
+ for (i = 0; i < NUM_MASKS; ++i) {
+ /* go through each keycode that is bound to the mask */
+ for (j = 0; j < modmap->max_keypermod; ++j) {
+ KeySym sym;
+ /* get a keycode that is bound to the mask (i) */
+ KeyCode keycode = modmap->modifiermap[i*modmap->max_keypermod + j];
+ if (keycode) {
+ /* go through each keysym bound to the given keycode */
+ for (k = 0; k < keysyms_per_keycode; ++k) {
+ sym = keymap[(keycode-min_keycode) * keysyms_per_keycode +
+ k];
+ if (sym != NoSymbol) {
+ /* bind the key to the mask (e.g. Alt_L => Mod1Mask) */
+ set_modkey_mask(nth_mask(i), sym);
+ }
+ }
+ }
+ }
+ }
+
+ /* CapsLock, Shift, and Control are special and hard-coded */
+ modkeys_keys[OBT_KEYBOARD_MODKEY_CAPSLOCK] = LockMask;
+ modkeys_keys[OBT_KEYBOARD_MODKEY_SHIFT] = ShiftMask;
+ modkeys_keys[OBT_KEYBOARD_MODKEY_CONTROL] = ControlMask;
+}
+
+void obt_keyboard_shutdown(void)
+{
+ GSList *it;
+
+ XFreeModifiermap(modmap);
+ modmap = NULL;
+ XFree(keymap);
+ keymap = NULL;
+ for (it = xic_all; it; it = g_slist_next(it)) {
+ ObtIC* ic = it->data;
+ if (ic->xic) {
+ XDestroyIC(ic->xic);
+ ic->xic = NULL;
+ }
+ }
+ if (xim) XCloseIM(xim);
+ xim = NULL;
+ xim_style = 0;
+ started = FALSE;
+}
+
+void xim_init(void)
+{
+ GSList *it;
+ gchar *aname, *aclass;
+
+ aname = g_strdup(g_get_prgname());
+ if (!aname) aname = g_strdup("obt");
+
+ aclass = g_strdup(aname);
+ if (g_ascii_islower(aclass[0]))
+ aclass[0] = g_ascii_toupper(aclass[0]);
+
+ xim = XOpenIM(obt_display, NULL, aname, aclass);
+
+ if (!xim)
+ g_message("Failed to open an Input Method");
+ else {
+ XIMStyles *xim_styles = NULL;
+ char *r;
+
+ /* get the input method styles */
+ r = XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL);
+ if (r || !xim_styles)
+ g_message("Input Method does not support any styles");
+ if (xim_styles) {
+ int i;
+
+ /* find a style that doesnt need preedit or status */
+ for (i = 0; i < xim_styles->count_styles; ++i) {
+ if (xim_styles->supported_styles[i] ==
+ (XIMPreeditNothing | XIMStatusNothing))
+ {
+ xim_style = xim_styles->supported_styles[i];
+ break;
+ }
+ }
+ XFree(xim_styles);
+ }
+
+ if (!xim_style) {
+ g_message("Input Method does not support a usable style");
+
+ XCloseIM(xim);
+ xim = NULL;
+ }
+ }
+
+ /* any existing contexts need to be recreated for the new input method */
+ for (it = xic_all; it; it = g_slist_next(it))
+ obt_keyboard_context_renew(it->data);
+
+ g_free(aclass);
+ g_free(aname);
+}
+
+ObtModkeysKey obt_keyboard_keyevent_to_modkey(XEvent *e)
+{
+ KeySym sym;
+
+ 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));
+}
+
+guint obt_keyboard_only_modmasks(guint mask)
+{
+ mask &= ALL_MASKS;
+ /* strip off these lock keys. they shouldn't affect key bindings */
+ mask &= ~LockMask; /* use the LockMask, not what capslock is bound to,
+ because you could bind it to something else and it
+ should work as that modifier then. i think capslock
+ is weird in xkb. */
+ mask &= ~obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_NUMLOCK);
+ mask &= ~obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SCROLLLOCK);
+ return mask;
+}
+
+guint obt_keyboard_modkey_to_modmask(ObtModkeysKey key)
+{
+ return modkeys_keys[key];
+}
+
+static void set_modkey_mask(guchar mask, KeySym sym)
+{
+ /* find what key this is, and bind it to the mask */
+
+ if (sym == XK_Num_Lock)
+ modkeys_keys[OBT_KEYBOARD_MODKEY_NUMLOCK] |= mask;
+ else if (sym == XK_Scroll_Lock)
+ modkeys_keys[OBT_KEYBOARD_MODKEY_SCROLLLOCK] |= mask;
+
+ else if (sym == XK_Super_L && super_l)
+ modkeys_keys[OBT_KEYBOARD_MODKEY_SUPER] |= mask;
+ else if (sym == XK_Super_L && !super_l)
+ /* left takes precident over right, so erase any masks the right
+ key may have set */
+ modkeys_keys[OBT_KEYBOARD_MODKEY_SUPER] = mask, super_l = TRUE;
+ else if (sym == XK_Super_R && !super_l)
+ modkeys_keys[OBT_KEYBOARD_MODKEY_SUPER] |= mask;
+
+ else if (sym == XK_Hyper_L && hyper_l)
+ modkeys_keys[OBT_KEYBOARD_MODKEY_HYPER] |= mask;
+ else if (sym == XK_Hyper_L && !hyper_l)
+ modkeys_keys[OBT_KEYBOARD_MODKEY_HYPER] = mask, hyper_l = TRUE;
+ else if (sym == XK_Hyper_R && !hyper_l)
+ modkeys_keys[OBT_KEYBOARD_MODKEY_HYPER] |= mask;
+
+ else if (sym == XK_Alt_L && alt_l)
+ modkeys_keys[OBT_KEYBOARD_MODKEY_ALT] |= mask;
+ else if (sym == XK_Alt_L && !alt_l)
+ modkeys_keys[OBT_KEYBOARD_MODKEY_ALT] = mask, alt_l = TRUE;
+ else if (sym == XK_Alt_R && !alt_l)
+ modkeys_keys[OBT_KEYBOARD_MODKEY_ALT] |= mask;
+
+ else if (sym == XK_Meta_L && meta_l)
+ modkeys_keys[OBT_KEYBOARD_MODKEY_META] |= mask;
+ else if (sym == XK_Meta_L && !meta_l)
+ modkeys_keys[OBT_KEYBOARD_MODKEY_META] = mask, meta_l = TRUE;
+ else if (sym == XK_Meta_R && !meta_l)
+ modkeys_keys[OBT_KEYBOARD_MODKEY_META] |= mask;
+
+ /* CapsLock, Shift, and Control are special and hard-coded */
+}
+
+KeyCode* obt_keyboard_keysym_to_keycode(KeySym sym)
+{
+ KeyCode *ret;
+ gint i, j, n;
+
+ ret = g_new(KeyCode, 1);
+ n = 0;
+ ret[n] = 0;
+
+ /* go through each keycode and look for the keysym */
+ for (i = min_keycode; i <= max_keycode; ++i)
+ for (j = 0; j < keysyms_per_keycode; ++j)
+ if (sym == keymap[(i-min_keycode) * keysyms_per_keycode + j]) {
+ ret = g_renew(KeyCode, ret, ++n + 1);
+ ret[n-1] = i;
+ ret[n] = 0;
+ }
+ return ret;
+}
+
+gunichar obt_keyboard_keypress_to_unichar(ObtIC *ic, XEvent *ev)
+{
+ gunichar unikey = 0;
+ KeySym sym;
+ Status status;
+ gchar *buf, fixbuf[4]; /* 4 is enough for a utf8 char */
+ gint len, bufsz;
+ gboolean got_string = FALSE;
+
+ g_return_val_if_fail(ev->type == KeyPress, 0);
+
+ if (!ic)
+ g_warning("Using obt_keyboard_keypress_to_unichar() without an "
+ "Input Context. No i18n support!");
+
+ if (ic && ic->xic) {
+ buf = fixbuf;
+ bufsz = sizeof(fixbuf);
+
+#ifdef X_HAVE_UTF8_STRING
+ len = Xutf8LookupString(ic->xic, &ev->xkey, buf, bufsz, &sym, &status);
+#else
+ len = XmbLookupString(ic->xic, &ev->xkey, buf, bufsz, &sym, &status);
+#endif
+
+ if (status == XBufferOverflow) {
+ buf = g_new(char, len);
+ bufsz = len;
+
+#ifdef X_HAVE_UTF8_STRING
+ len = Xutf8LookupString(ic->xic, &ev->xkey, buf, bufsz, &sym,
+ &status);
+#else
+ len = XmbLookupString(ic->xic, &ev->xkey, buf, bufsz, &sym,
+ &status);
+#endif
+ }
+
+ if ((status == XLookupChars || status == XLookupBoth)) {
+ if ((guchar)buf[0] >= 32) { /* not an ascii control character */
+#ifndef X_HAVE_UTF8_STRING
+ /* convert to utf8 */
+ gchar *buf2 = buf;
+ buf = g_locale_to_utf8(buf2, r, NULL, NULL, NULL);
+ g_free(buf2);
+#endif
+
+ got_string = TRUE;
+ }
+ }
+ else if (status == XLookupKeySym)
+ /* this key doesn't have a text representation, it is a command
+ key of some sort */;
+ else
+ g_message("Bad keycode lookup. Keysym 0x%x Status: %s\n",
+ (guint) sym,
+ (status == XBufferOverflow ? "BufferOverflow" :
+ status == XLookupNone ? "XLookupNone" :
+ status == XLookupKeySym ? "XLookupKeySym" :
+ "Unknown status"));
+ }
+ else {
+ buf = fixbuf;
+ bufsz = sizeof(fixbuf);
+ len = XLookupString(&ev->xkey, buf, bufsz, &sym, NULL);
+ if ((guchar)buf[0] >= 32) /* not an ascii control character */
+ got_string = TRUE;
+ }
+
+ if (got_string) {
+ gunichar u = g_utf8_get_char_validated(buf, len);
+ if (u && u != (gunichar)-1 && u != (gunichar)-2)
+ unikey = u;
+ }
+
+ if (buf != fixbuf) g_free(buf);
+
+ return unikey;
+}
+
+KeySym obt_keyboard_keypress_to_keysym(XEvent *ev)
+{
+ KeySym sym;
+
+ g_return_val_if_fail(ev->type == KeyPress, None);
+
+ sym = None;
+ XLookupString(&ev->xkey, NULL, 0, &sym, NULL);
+ return sym;
+}
+
+void obt_keyboard_context_renew(ObtIC *ic)
+{
+ if (xim) {
+ ic->xic = XCreateIC(xim,
+ XNInputStyle, xim_style,
+ XNClientWindow, ic->client,
+ XNFocusWindow, ic->focus,
+ NULL);
+ if (!ic->xic)
+ g_message("Error creating Input Context for window 0x%x 0x%x\n",
+ (guint)ic->client, (guint)ic->focus);
+ }
+}
+
+ObtIC* obt_keyboard_context_new(Window client, Window focus)
+{
+ ObtIC *ic;
+
+ g_return_val_if_fail(client != None && focus != None, NULL);
+
+ ic = g_slice_new(ObtIC);
+ ic->ref = 1;
+ ic->client = client;
+ ic->focus = focus;
+ ic->xic = NULL;
+
+ obt_keyboard_context_renew(ic);
+ xic_all = g_slist_prepend(xic_all, ic);
+
+ return ic;
+}
+
+void obt_keyboard_context_ref(ObtIC *ic)
+{
+ ++ic->ref;
+}
+
+void obt_keyboard_context_unref(ObtIC *ic)
+{
+ if (--ic->ref < 1) {
+ xic_all = g_slist_remove(xic_all, ic);
+ if (ic->xic)
+ XDestroyIC(ic->xic);
+ g_slice_free(ObtIC, ic);
+ }
+}
diff --git a/obt/keyboard.h b/obt/keyboard.h
new file mode 100644
index 0000000..868cccf
--- /dev/null
+++ b/obt/keyboard.h
@@ -0,0 +1,86 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/keyboard.h for the Openbox window manager
+ Copyright (c) 2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __obt_keyboard_h
+#define __obt_keyboard_h
+
+#include <glib.h>
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+
+G_BEGIN_DECLS
+
+/*! These keys are bound to the modifier masks in any fashion,
+ except for CapsLock, Shift, and Control. */
+typedef enum {
+ OBT_KEYBOARD_MODKEY_NONE,
+ OBT_KEYBOARD_MODKEY_CAPSLOCK,
+ OBT_KEYBOARD_MODKEY_NUMLOCK,
+ OBT_KEYBOARD_MODKEY_SCROLLLOCK,
+ OBT_KEYBOARD_MODKEY_SHIFT,
+ OBT_KEYBOARD_MODKEY_CONTROL,
+ OBT_KEYBOARD_MODKEY_SUPER,
+ OBT_KEYBOARD_MODKEY_HYPER,
+ OBT_KEYBOARD_MODKEY_META,
+ OBT_KEYBOARD_MODKEY_ALT,
+
+ OBT_KEYBOARD_NUM_MODKEYS
+} ObtModkeysKey;
+
+typedef struct _ObtIC ObtIC;
+
+void obt_keyboard_reload(void);
+
+/*! Get the modifier mask(s) for a keyboard event.
+ (eg. a keycode bound to Alt_L could return a mask of (Mod1Mask | Mask3Mask))
+*/
+guint obt_keyboard_keyevent_to_modmask(XEvent *e);
+
+/*! Strip off all modifiers except for the modifier keys. This strips stuff
+ like Button1Mask, and also LockMask, NumlockMask, and ScrolllockMask */
+guint obt_keyboard_only_modmasks(guint mask);
+
+/*! Get the modifier masks for a modifier key. This includes both the left and
+ 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);
+
+/*! Translate a KeyPress event to the unicode character it represents */
+gunichar obt_keyboard_keypress_to_unichar(ObtIC *ic, XEvent *ev);
+
+/*! Translate a KeyPress event to the KeySym that it represents. Use this
+ for control keys, not for getting text input! */
+KeySym obt_keyboard_keypress_to_keysym(XEvent *ev);
+
+/*! Create an input context for a window.
+ @client The top-level client window for the input context.
+ @focus The subwindow within the client for the input context.
+*/
+ObtIC* obt_keyboard_context_new(Window client, Window focus);
+
+void obt_keyboard_context_ref(ObtIC *ic);
+void obt_keyboard_context_unref(ObtIC *ic);
+
+G_END_DECLS
+
+#endif /* __obt_keyboard_h */
diff --git a/obt/link.c b/obt/link.c
new file mode 100644
index 0000000..9cc2bac
--- /dev/null
+++ b/obt/link.c
@@ -0,0 +1,238 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/link.c for the Openbox window manager
+ Copyright (c) 2009 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "obt/link.h"
+#include "obt/ddparse.h"
+#include "obt/paths.h"
+#include <glib.h>
+
+struct _ObtLink {
+ guint ref;
+
+ ObtLinkType type;
+ gchar *name; /*!< Specific name for the object (eg Firefox) */
+ gboolean display; /*<! When false, do not display this link in menus or
+ launchers, etc */
+ gboolean deleted; /*<! When true, the Link could exist but is deleted
+ for the current user */
+ gchar *generic; /*!< Generic name for the object (eg Web Browser) */
+ gchar *comment; /*!< Comment/description to display for the object */
+ gchar *icon; /*!< Name/path for an icon for the object */
+ guint env_required; /*!< The environments that must be present to use this
+ link. */
+ guint env_restricted; /*!< The environments that must _not_ be present to
+ use this link. */
+
+ union _ObtLinkData {
+ struct _ObtLinkApp {
+ gchar *exec; /*!< Executable to run for the app */
+ gchar *wdir; /*!< Working dir to run the app in */
+ gboolean term; /*!< Run the app in a terminal or not */
+ ObtLinkAppOpen open;
+
+ gchar **mime; /*!< Mime types the app can open */
+
+ GQuark *categories; /*!< Array of quarks representing the
+ application's categories */
+ gulong n_categories; /*!< Number of categories for the app */
+
+ ObtLinkAppStartup startup;
+ gchar *startup_wmclass;
+ } app;
+ struct _ObtLinkLink {
+ gchar *addr;
+ } url;
+ struct _ObtLinkDir {
+ } dir;
+ } d;
+};
+
+ObtLink* obt_link_from_ddfile(const gchar *ddname, GSList *paths,
+ ObtPaths *p)
+{
+ ObtLink *link;
+ GHashTable *groups, *keys;
+ ObtDDParseGroup *g;
+ ObtDDParseValue *v;
+
+ /* parse the file, and get a hash table of the groups */
+ groups = obt_ddparse_file(ddname, paths);
+ if (!groups) return NULL; /* parsing failed */
+ /* grab the Desktop Entry group */
+ g = g_hash_table_lookup(groups, "Desktop Entry");
+ g_assert(g != NULL);
+ /* grab the keys that appeared in the Desktop Entry group */
+ keys = obt_ddparse_group_keys(g);
+
+ /* build the ObtLink (we steal all strings from the parser) */
+ link = g_slice_new0(ObtLink);
+ link->ref = 1;
+ link->display = TRUE;
+
+ v = g_hash_table_lookup(keys, "Type");
+ g_assert(v);
+ link->type = v->value.enumerable;
+
+ if ((v = g_hash_table_lookup(keys, "Hidden")))
+ link->deleted = v->value.boolean;
+
+ if ((v = g_hash_table_lookup(keys, "NoDisplay")))
+ link->display = !v->value.boolean;
+
+ if ((v = g_hash_table_lookup(keys, "GenericName")))
+ link->generic = v->value.string, v->value.string = NULL;
+
+ if ((v = g_hash_table_lookup(keys, "Comment")))
+ link->comment = v->value.string, v->value.string = NULL;
+
+ if ((v = g_hash_table_lookup(keys, "Icon")))
+ link->icon = v->value.string, v->value.string = NULL;
+
+ if ((v = g_hash_table_lookup(keys, "OnlyShowIn")))
+ link->env_required = v->value.environments;
+ else
+ link->env_required = 0;
+
+ if ((v = g_hash_table_lookup(keys, "NotShowIn")))
+ link->env_restricted = v->value.environments;
+ else
+ link->env_restricted = 0;
+
+ /* type-specific keys */
+
+ if (link->type == OBT_LINK_TYPE_APPLICATION) {
+ gchar *c;
+ gboolean percent;
+
+ v = g_hash_table_lookup(keys, "Exec");
+ g_assert(v);
+ link->d.app.exec = v->value.string;
+ v->value.string = NULL;
+
+ /* parse link->d.app.exec to determine link->d.app.open */
+ percent = FALSE;
+ for (c = link->d.app.exec; *c; ++c) {
+ if (percent) {
+ switch (*c) {
+ case 'f': link->d.app.open = OBT_LINK_APP_SINGLE_LOCAL; break;
+ case 'F': link->d.app.open = OBT_LINK_APP_MULTI_LOCAL; break;
+ case 'u': link->d.app.open = OBT_LINK_APP_SINGLE_URL; break;
+ case 'U': link->d.app.open = OBT_LINK_APP_MULTI_URL; break;
+ default: percent = FALSE;
+ }
+ if (percent) break; /* found f/F/u/U */
+ }
+ else if (*c == '%') percent = TRUE;
+ }
+
+ if ((v = g_hash_table_lookup(keys, "TryExec"))) {
+ /* XXX spawn a thread to check TryExec? */
+ link->display = link->display &&
+ obt_paths_try_exec(p, v->value.string);
+ }
+
+ if ((v = g_hash_table_lookup(keys, "Path"))) {
+ /* steal the string */
+ link->d.app.wdir = v->value.string;
+ v->value.string = NULL;
+ }
+
+ if ((v = g_hash_table_lookup(keys, "Terminal")))
+ link->d.app.term = v->value.boolean;
+
+ if ((v = g_hash_table_lookup(keys, "StartupNotify")))
+ link->d.app.startup = v->value.boolean ?
+ OBT_LINK_APP_STARTUP_PROTOCOL_SUPPORT :
+ OBT_LINK_APP_STARTUP_NO_SUPPORT;
+ else {
+ link->d.app.startup = OBT_LINK_APP_STARTUP_LEGACY_SUPPORT;
+ if ((v = g_hash_table_lookup(keys, "StartupWMClass"))) {
+ /* steal the string */
+ link->d.app.startup_wmclass = v->value.string;
+ v->value.string = NULL;
+ }
+ }
+
+ if ((v = g_hash_table_lookup(keys, "Categories"))) {
+ gulong i;
+ gchar *end;
+
+ link->d.app.categories = g_new(GQuark, v->value.strings.n);
+ link->d.app.n_categories = v->value.strings.n;
+
+ for (i = 0; i < v->value.strings.n; ++i) {
+ link->d.app.categories[i] =
+ g_quark_from_string(v->value.strings.a[i]);
+ c = end = end+1; /* next */
+ }
+ }
+
+ if ((v = g_hash_table_lookup(keys, "MimeType"))) {
+ /* steal the string array */
+ link->d.app.mime = v->value.strings.a;
+ v->value.strings.a = NULL;
+ v->value.strings.n = 0;
+ }
+ }
+ else if (link->type == OBT_LINK_TYPE_URL) {
+ v = g_hash_table_lookup(keys, "URL");
+ g_assert(v);
+ link->d.url.addr = v->value.string;
+ v->value.string = NULL;
+ }
+
+ /* destroy the parsing info */
+ g_hash_table_destroy(groups);
+
+ return link;
+}
+
+void obt_link_ref(ObtLink *dd)
+{
+ ++dd->ref;
+}
+
+void obt_link_unref(ObtLink *dd)
+{
+ if (--dd->ref < 1) {
+ g_free(dd->name);
+ g_free(dd->generic);
+ g_free(dd->comment);
+ g_free(dd->icon);
+ if (dd->type == OBT_LINK_TYPE_APPLICATION) {
+ g_free(dd->d.app.exec);
+ g_free(dd->d.app.wdir);
+ g_strfreev(dd->d.app.mime);
+ g_free(dd->d.app.categories);
+ g_free(dd->d.app.startup_wmclass);
+ }
+ else if (dd->type == OBT_LINK_TYPE_URL)
+ g_free(dd->d.url.addr);
+ g_slice_free(ObtLink, dd);
+ }
+}
+
+const GQuark* obt_link_app_categories(ObtLink *e, gulong *n)
+{
+ g_return_val_if_fail(e != NULL, NULL);
+ g_return_val_if_fail(e->type == OBT_LINK_TYPE_APPLICATION, NULL);
+ g_return_val_if_fail(n != NULL, NULL);
+
+ *n = e->d.app.n_categories;
+ return e->d.app.categories;
+}
diff --git a/obt/link.h b/obt/link.h
new file mode 100644
index 0000000..9ad86cc
--- /dev/null
+++ b/obt/link.h
@@ -0,0 +1,116 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/link.h for the Openbox window manager
+ Copyright (c) 2009 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __obt_link_h
+#define __obt_link_h
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+struct _ObtPaths;
+
+typedef enum {
+ OBT_LINK_TYPE_APPLICATION = 1,
+ OBT_LINK_TYPE_URL = 2,
+ OBT_LINK_TYPE_DIRECTORY = 3
+} ObtLinkType;
+
+typedef enum {
+ OBT_LINK_APP_STARTUP_NO_SUPPORT,
+ OBT_LINK_APP_STARTUP_PROTOCOL_SUPPORT,
+ OBT_LINK_APP_STARTUP_LEGACY_SUPPORT
+} ObtLinkAppStartup;
+
+/*! These bit flags are environments for links. Some links are used or not
+ used in various environments. */
+typedef enum {
+ OBT_LINK_ENV_OPENBOX = 1 << 0,
+ OBT_LINK_ENV_GNOME = 1 << 1,
+ OBT_LINK_ENV_KDE = 1 << 2,
+ OBT_LINK_ENV_LXDE = 1 << 3,
+ OBT_LINK_ENV_ROX = 1 << 4,
+ OBT_LINK_ENV_XFCE = 1 << 5,
+ OBT_LINK_ENV_OLD = 1 << 6
+} ObtLinkEnvFlags;
+
+typedef enum {
+ /*! The app can be launched with a single local file */
+ OBT_LINK_APP_SINGLE_LOCAL = 1 << 0,
+ /*! The app can be launched with multiple local files */
+ OBT_LINK_APP_MULTI_LOCAL = 1 << 1,
+ /*! The app can be launched with a single URL */
+ OBT_LINK_APP_SINGLE_URL = 1 << 2,
+ /*! The app can be launched with multiple URLs */
+ OBT_LINK_APP_MULTI_URL = 1 << 3
+} ObtLinkAppOpen;
+
+typedef struct _ObtLink ObtLink;
+
+ObtLink* obt_link_from_ddfile(const gchar *name, GSList *paths,
+ struct _ObtPaths *p);
+
+void obt_link_ref(ObtLink *e);
+void obt_link_unref(ObtLink *e);
+
+/*! Returns TRUE if the file exists but says it should be ignored, with
+ the Hidden flag. No other functions can be used for the ObtLink
+ in this case. */
+gboolean obt_link_deleted (ObtLink *e);
+
+/*! Returns the type of object refered to by the .desktop file. */
+ObtLinkType obt_link_type (ObtLink *e);
+
+/*! Returns TRUE if the .desktop file should be displayed to users, given the
+ current environment. If FALSE, the .desktop file should not be showed.
+ This also uses the TryExec option if it is present.
+ @env A semicolon-deliminated list of environemnts. Can be one or more of:
+ GNOME, KDE, ROX, XFCE. Other environments not listed here may also
+ be supported. This can be null also if not listing any environment. */
+gboolean obt_link_display(ObtLink *e, const gchar *env);
+
+const gchar* obt_link_name (ObtLink *e);
+const gchar* obt_link_generic_name (ObtLink *e);
+const gchar* obt_link_comment (ObtLink *e);
+/*! Returns the icon for the object referred to by the .desktop file.
+ Returns either an absolute path, or a string which can be used to find the
+ icon using the algorithm given by:
+ http://freedesktop.org/wiki/Specifications/icon-theme-spec?action=show&redirect=Standards/icon-theme-spec
+*/
+const gchar* obt_link_icon (ObtLink *e);
+
+const gchar *obt_link_url_path(ObtLink *e);
+
+const gchar* obt_link_app_executable (ObtLink *e);
+/*! Returns the path in which the application should be run */
+const gchar* obt_link_app_path (ObtLink *e);
+gboolean obt_link_app_run_in_terminal (ObtLink *e);
+const gchar*const* obt_link_app_mime_types (ObtLink *e);
+const GQuark* obt_link_app_categories (ObtLink *e, gulong *n);
+/*! Returns a combination of values in the ObtLinkAppOpen enum,
+ specifying if the application can be launched to open one or more files
+ and URLs. */
+ObtLinkAppOpen obt_link_app_open(ObtLink *e);
+
+ObtLinkAppStartup obt_link_app_startup_notify(ObtLink *e);
+const gchar* obt_link_app_startup_wmclass(ObtLink *e);
+
+
+G_END_DECLS
+
+#endif
diff --git a/obt/obt-3.5.pc.in b/obt/obt-3.5.pc.in
new file mode 100644
index 0000000..8dae581
--- /dev/null
+++ b/obt/obt-3.5.pc.in
@@ -0,0 +1,14 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+xcflags=@X_CFLAGS@
+xlibs=@X_LIBS@
+
+Name: Obt
+Description: Openbox Toolkit Library
+Version: @OBT_VERSION@
+Requires: glib-2.0 libxml-2.0
+Libs: -L${libdir} -lobt ${xlibs}
+Cflags: -I${includedir}/openbox/@OBT_VERSION@ ${xcflags}
diff --git a/obt/paths.c b/obt/paths.c
new file mode 100644
index 0000000..d2e230d
--- /dev/null
+++ b/obt/paths.c
@@ -0,0 +1,368 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/paths.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "obt/bsearch.h"
+#include "obt/paths.h"
+#include "obt/util.h"
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_GRP_H
+# include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+# include <pwd.h>
+#endif
+
+struct _ObtPaths
+{
+ gint ref;
+ gchar *config_home;
+ gchar *data_home;
+ gchar *cache_home;
+ GSList *config_dirs;
+ GSList *data_dirs;
+ GSList *autostart_dirs;
+ GSList *exec_dirs;
+
+ uid_t uid;
+ gid_t *gid;
+ guint n_gid;
+};
+
+static gint slist_path_cmp(const gchar *a, const gchar *b)
+{
+ return strcmp(a, b);
+}
+
+typedef GSList* (*GSListFunc) (gpointer list, gconstpointer data);
+
+static GSList* slist_path_add(GSList *list, gpointer data, GSListFunc func)
+{
+ g_assert(func);
+
+ if (!data)
+ return list;
+
+ if (!g_slist_find_custom(list, data, (GCompareFunc) slist_path_cmp))
+ list = func(list, data);
+ else
+ g_free(data);
+
+ return list;
+}
+
+static GSList* split_paths(const gchar *paths)
+{
+ GSList *list = NULL;
+ gchar **spl, **it;
+
+ if (!paths)
+ return NULL;
+ spl = g_strsplit(paths, ":", -1);
+ for (it = spl; *it; ++it)
+ list = slist_path_add(list, *it, (GSListFunc) g_slist_append);
+ g_free(spl);
+ return list;
+}
+
+int gid_cmp(const void *va, const void *vb)
+{
+ const gid_t a = *(const gid_t*)va, b = *(const gid_t*)vb;
+ return a>b ? 1 : (a == b ? 0 : -1);
+}
+
+static void find_uid_gid(uid_t *u, gid_t **g, guint *n)
+{
+ struct passwd *pw;
+ const gchar *name;
+ struct group *gr;
+
+ *u = getuid();
+ pw = getpwuid(*u);
+ name = pw->pw_name;
+
+ *g = g_new(gid_t, *n=1);
+ (*g)[0] = getgid();
+
+ while ((gr = getgrent())) {
+ if (gr->gr_gid != (*g)[0]) { /* skip the main group */
+ gchar **c;
+ for (c = gr->gr_mem; *c; ++c)
+ if (strcmp(*c, name) == 0) {
+ *g = g_renew(gid_t, *g, ++(*n)); /* save the group */
+ (*g)[*n-1] = gr->gr_gid;
+ break;
+ }
+ }
+ }
+ endgrent();
+
+ qsort(*g, *n, sizeof(gid_t), gid_cmp);
+}
+
+ObtPaths* obt_paths_new(void)
+{
+ ObtPaths *p;
+ const gchar *path;
+ GSList *it;
+
+ p = g_slice_new0(ObtPaths);
+ p->ref = 1;
+
+ find_uid_gid(&p->uid, &p->gid, &p->n_gid);
+
+ path = g_getenv("XDG_CONFIG_HOME");
+ if (path && path[0] != '\0') /* not unset or empty */
+ p->config_home = g_build_filename(path, NULL);
+ else
+ p->config_home = g_build_filename(g_get_home_dir(), ".config", NULL);
+
+ path = g_getenv("XDG_DATA_HOME");
+ if (path && path[0] != '\0') /* not unset or empty */
+ p->data_home = g_build_filename(path, NULL);
+ else
+ p->data_home = g_build_filename(g_get_home_dir(), ".local",
+ "share", NULL);
+
+ path = g_getenv("XDG_CACHE_HOME");
+ if (path && path[0] != '\0') /* not unset or empty */
+ p->cache_home = g_build_filename(path, NULL);
+ else
+ p->cache_home = g_build_filename(g_get_home_dir(), ".cache", NULL);
+
+ path = g_getenv("XDG_CONFIG_DIRS");
+ if (path && path[0] != '\0') /* not unset or empty */
+ p->config_dirs = split_paths(path);
+ else {
+ p->config_dirs = slist_path_add(p->config_dirs,
+ g_strdup(CONFIGDIR),
+ (GSListFunc) g_slist_append);
+ p->config_dirs = slist_path_add(p->config_dirs,
+ g_build_filename
+ (G_DIR_SEPARATOR_S,
+ "etc", "xdg", NULL),
+ (GSListFunc) g_slist_append);
+ }
+ p->config_dirs = slist_path_add(p->config_dirs,
+ g_strdup(p->config_home),
+ (GSListFunc) g_slist_prepend);
+
+ for (it = p->config_dirs; it; it = g_slist_next(it)) {
+ gchar *const s = g_strdup_printf("%s/autostart", (gchar*)it->data);
+ p->autostart_dirs = g_slist_append(p->autostart_dirs, s);
+ }
+
+ path = g_getenv("XDG_DATA_DIRS");
+ if (path && path[0] != '\0') /* not unset or empty */
+ p->data_dirs = split_paths(path);
+ else {
+ p->data_dirs = slist_path_add(p->data_dirs,
+ g_strdup(DATADIR),
+ (GSListFunc) g_slist_append);
+ p->data_dirs = slist_path_add(p->data_dirs,
+ g_build_filename
+ (G_DIR_SEPARATOR_S,
+ "usr", "local", "share", NULL),
+ (GSListFunc) g_slist_append);
+ p->data_dirs = slist_path_add(p->data_dirs,
+ g_build_filename
+ (G_DIR_SEPARATOR_S,
+ "usr", "share", NULL),
+ (GSListFunc) g_slist_append);
+ }
+ p->data_dirs = slist_path_add(p->data_dirs,
+ g_strdup(p->data_home),
+ (GSListFunc) g_slist_prepend);
+
+ path = g_getenv("PATH");
+ if (path && path[0] != '\0') /* not unset or empty */
+ p->exec_dirs = split_paths(path);
+ else
+ p->exec_dirs = NULL;
+
+ return p;
+}
+
+void obt_paths_ref(ObtPaths *p)
+{
+ ++p->ref;
+}
+
+void obt_paths_unref(ObtPaths *p)
+{
+ if (p && --p->ref == 0) {
+ GSList *it;
+
+ for (it = p->config_dirs; it; it = g_slist_next(it))
+ g_free(it->data);
+ g_slist_free(p->config_dirs);
+ for (it = p->data_dirs; it; it = g_slist_next(it))
+ g_free(it->data);
+ g_slist_free(p->data_dirs);
+ for (it = p->autostart_dirs; it; it = g_slist_next(it))
+ g_free(it->data);
+ g_slist_free(p->autostart_dirs);
+ for (it = p->exec_dirs; it; it = g_slist_next(it))
+ g_free(it->data);
+ g_slist_free(p->exec_dirs);
+ g_free(p->config_home);
+ g_free(p->data_home);
+ g_free(p->cache_home);
+ g_free(p->gid);
+
+ g_slice_free(ObtPaths, p);
+ }
+}
+
+gchar *obt_paths_expand_tilde(const gchar *f)
+{
+ gchar *ret;
+ GRegex *regex;
+
+ if (!f)
+ return NULL;
+
+ regex = g_regex_new("(?:^|(?<=[ \\t]))~(?=[/ \\t$])", G_REGEX_MULTILINE | G_REGEX_RAW, 0, NULL);
+ ret = g_regex_replace_literal(regex, f, -1, 0, g_get_home_dir(), 0, NULL);
+ g_regex_unref(regex);
+
+ return ret;
+}
+
+gboolean obt_paths_mkdir(const gchar *path, gint mode)
+{
+ gboolean ret = TRUE;
+
+ g_return_val_if_fail(path != NULL, FALSE);
+ g_return_val_if_fail(path[0] != '\0', FALSE);
+
+ if (!g_file_test(path, G_FILE_TEST_IS_DIR))
+ if (mkdir(path, mode) == -1)
+ ret = FALSE;
+
+ return ret;
+}
+
+gboolean obt_paths_mkdir_path(const gchar *path, gint mode)
+{
+ gboolean ret = TRUE;
+
+ g_return_val_if_fail(path != NULL, FALSE);
+ g_return_val_if_fail(path[0] == '/', FALSE);
+
+ if (!g_file_test(path, G_FILE_TEST_IS_DIR)) {
+ gchar *c, *e;
+
+ c = g_strdup(path);
+ e = c;
+ while ((e = strchr(e + 1, '/'))) {
+ *e = '\0';
+ if (!(ret = obt_paths_mkdir(c, mode)))
+ goto parse_mkdir_path_end;
+ *e = '/';
+ }
+ ret = obt_paths_mkdir(c, mode);
+
+ parse_mkdir_path_end:
+ g_free(c);
+ }
+
+ return ret;
+}
+
+const gchar* obt_paths_config_home(ObtPaths *p)
+{
+ return p->config_home;
+}
+
+const gchar* obt_paths_data_home(ObtPaths *p)
+{
+ return p->data_home;
+}
+
+const gchar* obt_paths_cache_home(ObtPaths *p)
+{
+ return p->cache_home;
+}
+
+GSList* obt_paths_config_dirs(ObtPaths *p)
+{
+ return p->config_dirs;
+}
+
+GSList* obt_paths_data_dirs(ObtPaths *p)
+{
+ return p->data_dirs;
+}
+
+GSList* obt_paths_autostart_dirs(ObtPaths *p)
+{
+ return p->autostart_dirs;
+}
+
+static inline gboolean try_exec(const ObtPaths *const p,
+ const gchar *const path)
+{
+ struct stat st;
+ BSEARCH_SETUP(guint);
+
+ if (stat(path, &st) != 0)
+ return FALSE;
+
+ if (!S_ISREG(st.st_mode))
+ return FALSE;
+ if (st.st_uid == p->uid)
+ return st.st_mode & S_IXUSR;
+ BSEARCH(guint, p->gid, 0, p->n_gid, st.st_gid);
+ if (BSEARCH_FOUND())
+ return st.st_mode & S_IXGRP;
+ return st.st_mode & S_IXOTH;
+}
+
+gboolean obt_paths_try_exec(ObtPaths *p, const gchar *path)
+{
+ if (path[0] == '/') {
+ return try_exec(p, path);
+ }
+ else {
+ 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);
+ gboolean e = try_exec(p, f);
+ g_free(f);
+ if (e) return TRUE;
+ }
+ }
+
+ return FALSE;
+}
diff --git a/obt/paths.h b/obt/paths.h
new file mode 100644
index 0000000..7c43682
--- /dev/null
+++ b/obt/paths.h
@@ -0,0 +1,50 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/paths.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __obt_paths_h
+#define __obt_paths_h
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ObtPaths ObtPaths;
+
+ObtPaths* obt_paths_new(void);
+void obt_paths_ref(ObtPaths *p);
+void obt_paths_unref(ObtPaths *p);
+
+const gchar* obt_paths_config_home(ObtPaths *p);
+const gchar* obt_paths_data_home(ObtPaths *p);
+const gchar* obt_paths_cache_home(ObtPaths *p);
+GSList* obt_paths_config_dirs(ObtPaths *p);
+GSList* obt_paths_data_dirs(ObtPaths *p);
+GSList* obt_paths_autostart_dirs(ObtPaths *p);
+
+gchar *obt_paths_expand_tilde(const gchar *f);
+gboolean obt_paths_mkdir(const gchar *path, gint mode);
+gboolean obt_paths_mkdir_path(const gchar *path, gint mode);
+
+/*! Returns TRUE if the @path points to an executable file.
+ If the @path is not an absolute path, then it is searched for in $PATH.
+*/
+gboolean obt_paths_try_exec(ObtPaths *p, const gchar *path);
+
+G_END_DECLS
+
+#endif
diff --git a/obt/prop.c b/obt/prop.c
new file mode 100644
index 0000000..638373f
--- /dev/null
+++ b/obt/prop.c
@@ -0,0 +1,584 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/prop.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "obt/prop.h"
+#include "obt/display.h"
+
+#include <X11/Xatom.h>
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+
+Atom prop_atoms[OBT_PROP_NUM_ATOMS];
+gboolean prop_started = FALSE;
+
+#define CREATE_NAME(var, name) (prop_atoms[OBT_PROP_##var] = \
+ XInternAtom((obt_display), (name), FALSE))
+#define CREATE(var) CREATE_NAME(var, #var)
+#define CREATE_(var) CREATE_NAME(var, "_" #var)
+
+void obt_prop_startup(void)
+{
+ if (prop_started) return;
+ prop_started = TRUE;
+
+ g_assert(obt_display);
+
+ CREATE(CARDINAL);
+ CREATE(WINDOW);
+ CREATE(PIXMAP);
+ CREATE(ATOM);
+ CREATE(STRING);
+ CREATE(COMPOUND_TEXT);
+ CREATE(UTF8_STRING);
+
+ CREATE(MANAGER);
+
+ CREATE(WM_COLORMAP_WINDOWS);
+ CREATE(WM_PROTOCOLS);
+ CREATE(WM_STATE);
+ CREATE(WM_CHANGE_STATE);
+ CREATE(WM_DELETE_WINDOW);
+ CREATE(WM_TAKE_FOCUS);
+ CREATE(WM_NAME);
+ CREATE(WM_ICON_NAME);
+ CREATE(WM_CLASS);
+ CREATE(WM_WINDOW_ROLE);
+ CREATE(WM_CLIENT_MACHINE);
+ CREATE(WM_COMMAND);
+ CREATE(WM_CLIENT_LEADER);
+ CREATE(WM_TRANSIENT_FOR);
+ CREATE_(MOTIF_WM_HINTS);
+ CREATE_(MOTIF_WM_INFO);
+
+ CREATE(SM_CLIENT_ID);
+
+ CREATE_(NET_WM_FULL_PLACEMENT);
+
+ CREATE_(NET_SUPPORTED);
+ CREATE_(NET_CLIENT_LIST);
+ CREATE_(NET_CLIENT_LIST_STACKING);
+ CREATE_(NET_NUMBER_OF_DESKTOPS);
+ CREATE_(NET_DESKTOP_GEOMETRY);
+ CREATE_(NET_DESKTOP_VIEWPORT);
+ CREATE_(NET_CURRENT_DESKTOP);
+ CREATE_(NET_DESKTOP_NAMES);
+ CREATE_(NET_ACTIVE_WINDOW);
+/* CREATE_(NET_RESTACK_WINDOW);*/
+ CREATE_(NET_WORKAREA);
+ CREATE_(NET_SUPPORTING_WM_CHECK);
+ CREATE_(NET_DESKTOP_LAYOUT);
+ CREATE_(NET_SHOWING_DESKTOP);
+
+ CREATE_(NET_CLOSE_WINDOW);
+ CREATE_(NET_WM_MOVERESIZE);
+ CREATE_(NET_MOVERESIZE_WINDOW);
+ CREATE_(NET_REQUEST_FRAME_EXTENTS);
+ CREATE_(NET_RESTACK_WINDOW);
+
+ CREATE_(NET_STARTUP_ID);
+
+ CREATE_(NET_WM_NAME);
+ CREATE_(NET_WM_VISIBLE_NAME);
+ CREATE_(NET_WM_ICON_NAME);
+ CREATE_(NET_WM_VISIBLE_ICON_NAME);
+ CREATE_(NET_WM_DESKTOP);
+ CREATE_(NET_WM_WINDOW_TYPE);
+ CREATE_(NET_WM_STATE);
+ CREATE_(NET_WM_STRUT);
+ CREATE_(NET_WM_STRUT_PARTIAL);
+ CREATE_(NET_WM_ICON);
+ CREATE_(NET_WM_ICON_GEOMETRY);
+ CREATE_(NET_WM_PID);
+ CREATE_(NET_WM_ALLOWED_ACTIONS);
+ CREATE_(NET_WM_WINDOW_OPACITY);
+ CREATE_(NET_WM_USER_TIME);
+/* CREATE_(NET_WM_USER_TIME_WINDOW); */
+ CREATE_(KDE_NET_WM_FRAME_STRUT);
+ CREATE_(NET_FRAME_EXTENTS);
+
+ CREATE_(NET_WM_PING);
+#ifdef SYNC
+ CREATE_(NET_WM_SYNC_REQUEST);
+ CREATE_(NET_WM_SYNC_REQUEST_COUNTER);
+#endif
+
+ CREATE_(NET_WM_WINDOW_TYPE_DESKTOP);
+ CREATE_(NET_WM_WINDOW_TYPE_DOCK);
+ CREATE_(NET_WM_WINDOW_TYPE_TOOLBAR);
+ CREATE_(NET_WM_WINDOW_TYPE_MENU);
+ CREATE_(NET_WM_WINDOW_TYPE_UTILITY);
+ CREATE_(NET_WM_WINDOW_TYPE_SPLASH);
+ CREATE_(NET_WM_WINDOW_TYPE_DIALOG);
+ CREATE_(NET_WM_WINDOW_TYPE_NORMAL);
+ CREATE_(NET_WM_WINDOW_TYPE_POPUP_MENU);
+
+ prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPLEFT] = 0;
+ prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOP] = 1;
+ prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPRIGHT] = 2;
+ prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_RIGHT] = 3;
+ prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT] = 4;
+ prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOM] = 5;
+ prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT] = 6;
+ prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_LEFT] = 7;
+ prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_MOVE] = 8;
+ prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_KEYBOARD] = 9;
+ prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_MOVE_KEYBOARD] = 10;
+ prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_CANCEL] = 11;
+
+ CREATE_(NET_WM_ACTION_MOVE);
+ CREATE_(NET_WM_ACTION_RESIZE);
+ CREATE_(NET_WM_ACTION_MINIMIZE);
+ CREATE_(NET_WM_ACTION_SHADE);
+ CREATE_(NET_WM_ACTION_MAXIMIZE_HORZ);
+ CREATE_(NET_WM_ACTION_MAXIMIZE_VERT);
+ CREATE_(NET_WM_ACTION_FULLSCREEN);
+ CREATE_(NET_WM_ACTION_CHANGE_DESKTOP);
+ CREATE_(NET_WM_ACTION_CLOSE);
+ CREATE_(NET_WM_ACTION_ABOVE);
+ CREATE_(NET_WM_ACTION_BELOW);
+
+ CREATE_(NET_WM_STATE_MODAL);
+/* CREATE_(NET_WM_STATE_STICKY);*/
+ CREATE_(NET_WM_STATE_MAXIMIZED_VERT);
+ CREATE_(NET_WM_STATE_MAXIMIZED_HORZ);
+ CREATE_(NET_WM_STATE_SHADED);
+ CREATE_(NET_WM_STATE_SKIP_TASKBAR);
+ CREATE_(NET_WM_STATE_SKIP_PAGER);
+ CREATE_(NET_WM_STATE_HIDDEN);
+ CREATE_(NET_WM_STATE_FULLSCREEN);
+ CREATE_(NET_WM_STATE_ABOVE);
+ CREATE_(NET_WM_STATE_BELOW);
+ CREATE_(NET_WM_STATE_DEMANDS_ATTENTION);
+
+ prop_atoms[OBT_PROP_NET_WM_STATE_ADD] = 1;
+ prop_atoms[OBT_PROP_NET_WM_STATE_REMOVE] = 0;
+ prop_atoms[OBT_PROP_NET_WM_STATE_TOGGLE] = 2;
+
+ prop_atoms[OBT_PROP_NET_WM_ORIENTATION_HORZ] = 0;
+ prop_atoms[OBT_PROP_NET_WM_ORIENTATION_VERT] = 1;
+ prop_atoms[OBT_PROP_NET_WM_TOPLEFT] = 0;
+ prop_atoms[OBT_PROP_NET_WM_TOPRIGHT] = 1;
+ prop_atoms[OBT_PROP_NET_WM_BOTTOMRIGHT] = 2;
+ prop_atoms[OBT_PROP_NET_WM_BOTTOMLEFT] = 3;
+
+ CREATE_(KDE_WM_CHANGE_STATE);
+ CREATE_(KDE_NET_WM_WINDOW_TYPE_OVERRIDE);
+
+/*
+ CREATE_NAME(ROOTPMAPId, "_XROOTPMAP_ID");
+ CREATE_NAME(ESETROOTId, "ESETROOT_PMAP_ID");
+*/
+
+ CREATE_(OPENBOX_PID);
+ CREATE_(OB_THEME);
+ CREATE_(OB_CONFIG_FILE);
+ CREATE_(OB_WM_ACTION_UNDECORATE);
+ CREATE_(OB_WM_STATE_UNDECORATED);
+ CREATE_(OB_CONTROL);
+ CREATE_(OB_VERSION);
+ CREATE_(OB_APP_ROLE);
+ CREATE_(OB_APP_TITLE);
+ CREATE_(OB_APP_NAME);
+ CREATE_(OB_APP_CLASS);
+ CREATE_(OB_APP_TYPE);
+}
+
+Atom obt_prop_atom(ObtPropAtom a)
+{
+ g_assert(prop_started);
+ g_assert(a < OBT_PROP_NUM_ATOMS);
+ return prop_atoms[a];
+}
+
+static gboolean get_prealloc(Window win, Atom prop, Atom type, gint size,
+ guchar *data, gulong num)
+{
+ gboolean ret = FALSE;
+ gint res;
+ guchar *xdata = NULL;
+ Atom ret_type;
+ gint ret_size;
+ gulong ret_items, bytes_left;
+ glong num32 = 32 / size * num; /* num in 32-bit elements */
+
+ res = XGetWindowProperty(obt_display, win, prop, 0l, num32,
+ FALSE, type, &ret_type, &ret_size,
+ &ret_items, &bytes_left, &xdata);
+ if (res == Success && ret_items && xdata) {
+ if (ret_size == size && ret_items >= num) {
+ guint i;
+ for (i = 0; i < num; ++i)
+ switch (size) {
+ case 8:
+ data[i] = xdata[i];
+ break;
+ case 16:
+ ((guint16*)data)[i] = ((gushort*)xdata)[i];
+ break;
+ case 32:
+ ((guint32*)data)[i] = ((gulong*)xdata)[i];
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled size */
+ }
+ ret = TRUE;
+ }
+ XFree(xdata);
+ }
+ return ret;
+}
+
+static gboolean get_all(Window win, Atom prop, Atom type, gint size,
+ guchar **data, guint *num)
+{
+ gboolean ret = FALSE;
+ gint res;
+ guchar *xdata = NULL;
+ Atom ret_type;
+ gint ret_size;
+ gulong ret_items, bytes_left;
+
+ res = XGetWindowProperty(obt_display, win, prop, 0l, G_MAXLONG,
+ FALSE, type, &ret_type, &ret_size,
+ &ret_items, &bytes_left, &xdata);
+ if (res == Success) {
+ if (ret_size == size && ret_items > 0) {
+ guint i;
+
+ *data = g_malloc(ret_items * (size / 8));
+ for (i = 0; i < ret_items; ++i)
+ switch (size) {
+ case 8:
+ (*data)[i] = xdata[i];
+ break;
+ case 16:
+ ((guint16*)*data)[i] = ((gushort*)xdata)[i];
+ break;
+ case 32:
+ ((guint32*)*data)[i] = ((gulong*)xdata)[i];
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled size */
+ }
+ *num = ret_items;
+ ret = TRUE;
+ }
+ XFree(xdata);
+ }
+ return ret;
+}
+
+/*! Get a text property from a window, and fill out the XTextProperty with it.
+ @param win The window to read the property from.
+ @param prop The atom of the property to read off the window.
+ @param tprop The XTextProperty to fill out.
+ @param type 0 to get text of any type, or a value from
+ ObtPropTextType to restrict the value to a specific type.
+ @return TRUE if the text was read and validated against the @type, and FALSE
+ otherwise.
+*/
+static gboolean get_text_property(Window win, Atom prop,
+ XTextProperty *tprop, ObtPropTextType type)
+{
+ if (!(XGetTextProperty(obt_display, win, tprop, prop) && tprop->nitems))
+ return FALSE;
+ if (!type)
+ return TRUE; /* no type checking */
+ switch (type) {
+ case OBT_PROP_TEXT_STRING:
+ case OBT_PROP_TEXT_STRING_XPCS:
+ case OBT_PROP_TEXT_STRING_NO_CC:
+ return tprop->encoding == OBT_PROP_ATOM(STRING);
+ case OBT_PROP_TEXT_COMPOUND_TEXT:
+ return tprop->encoding == OBT_PROP_ATOM(COMPOUND_TEXT);
+ case OBT_PROP_TEXT_UTF8_STRING:
+ return tprop->encoding == OBT_PROP_ATOM(UTF8_STRING);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+/*! Returns one or more UTF-8 encoded strings from the text property.
+ @param tprop The XTextProperty to convert into UTF-8 string(s).
+ @param type The type which specifies the format that the text must meet, or
+ 0 to allow any valid characters that can be converted to UTF-8 through.
+ @param max The maximum number of strings to return. -1 to return them all.
+ @return If max is 1, then this returns a gchar* with the single string.
+ Otherwise, this returns a gchar** of no more than max strings (or all
+ strings read, if max is negative). If an error occurs, NULL is returned.
+ */
+static void* convert_text_property(XTextProperty *tprop,
+ ObtPropTextType type, gint max)
+{
+ enum {
+ LATIN1,
+ UTF8,
+ LOCALE
+ } encoding;
+ const gboolean return_single = (max == 1);
+ gboolean ok = FALSE;
+ gchar **strlist = NULL;
+ gchar *single[1] = { NULL };
+ gchar **retlist = single; /* single is used when max == 1 */
+ gint i, n_strs;
+
+ /* Read each string in the text property and store a pointer to it in
+ retlist. These pointers point into the X data structures directly.
+
+ Then we will convert them to UTF-8, and replace the retlist pointer with
+ a new one.
+ */
+ if (tprop->encoding == OBT_PROP_ATOM(COMPOUND_TEXT))
+ {
+ encoding = LOCALE;
+ ok = (XmbTextPropertyToTextList(
+ obt_display, tprop, &strlist, &n_strs) == Success);
+ if (ok) {
+ if (max >= 0)
+ n_strs = MIN(max, n_strs);
+ if (!return_single)
+ retlist = g_new0(gchar*, n_strs+1);
+ if (retlist)
+ for (i = 0; i < n_strs; ++i)
+ retlist[i] = strlist[i];
+ }
+ }
+ else if (tprop->encoding == OBT_PROP_ATOM(UTF8_STRING) ||
+ tprop->encoding == OBT_PROP_ATOM(STRING))
+ {
+ gchar *p; /* iterator */
+
+ if (tprop->encoding == OBT_PROP_ATOM(STRING))
+ encoding = LATIN1;
+ else
+ encoding = UTF8;
+ ok = TRUE;
+
+ /* First, count the number of strings. Then make a structure for them
+ and copy pointers to them into it. */
+ p = (gchar*)tprop->value;
+ n_strs = 0;
+ while (p < (gchar*)tprop->value + tprop->nitems) {
+ p += strlen(p) + 1; /* next string */
+ ++n_strs;
+ }
+
+ if (max >= 0)
+ n_strs = MIN(max, n_strs);
+ if (!return_single)
+ retlist = g_new0(gchar*, n_strs+1);
+ if (retlist) {
+ p = (gchar*)tprop->value;
+ for (i = 0; i < n_strs; ++i) {
+ retlist[i] = p;
+ p += strlen(p) + 1; /* next string */
+ }
+ }
+ }
+
+ if (!(ok && retlist)) {
+ if (strlist) XFreeStringList(strlist);
+ return NULL;
+ }
+
+ /* convert each element in retlist to UTF-8, and replace it. */
+ for (i = 0; i < n_strs; ++i) {
+ if (encoding == UTF8) {
+ const gchar *end; /* the first byte past the valid data */
+
+ g_utf8_validate(retlist[i], -1, &end);
+ retlist[i] = g_strndup(retlist[i], end-retlist[i]);
+ }
+ else if (encoding == LOCALE) {
+ gsize nvalid; /* the number of valid bytes at the front of the
+ string */
+ gchar *utf; /* the string converted into utf8 */
+
+ utf = g_locale_to_utf8(retlist[i], -1, &nvalid, NULL, NULL);
+ if (!utf)
+ utf = g_locale_to_utf8(retlist[i], nvalid, NULL, NULL, NULL);
+ g_assert(utf);
+ retlist[i] = utf;
+ }
+ else { /* encoding == LATIN1 */
+ gsize nvalid; /* the number of valid bytes at the front of the
+ string */
+ gchar *utf; /* the string converted into utf8 */
+ gchar *p; /* iterator */
+
+ /* look for invalid characters */
+ for (p = retlist[i], nvalid = 0; *p; ++p, ++nvalid) {
+ /* The only valid control characters are TAB(HT)=9 and
+ NEWLINE(LF)=10.
+ This is defined in ICCCM section 2:
+ http://tronche.com/gui/x/icccm/sec-2.html.
+ See a definition of the latin1 codepage here:
+ http://en.wikipedia.org/wiki/ISO/IEC_8859-1.
+ The above page includes control characters in the table,
+ which we must explicitly exclude, as the g_convert function
+ will happily take them.
+ */
+ const register guchar c = (guchar)*p; /* unsigned value at p */
+ if ((c < 32 && c != 9 && c != 10) || (c >= 127 && c <= 160))
+ break; /* found a control character that isn't allowed */
+
+ if (type == OBT_PROP_TEXT_STRING_NO_CC && c < 32)
+ break; /* absolutely no control characters are allowed */
+
+ if (type == OBT_PROP_TEXT_STRING_XPCS) {
+ const gboolean valid = (
+ (c >= 32 && c < 128) || c == 9 || c == 10);
+ if (!valid)
+ break; /* strict whitelisting for XPCS */
+ }
+ }
+ /* look for invalid latin1 characters */
+ utf = g_convert(retlist[i], nvalid, "utf-8", "iso-8859-1",
+ &nvalid, NULL, NULL);
+ if (!utf)
+ utf = g_convert(retlist[i], nvalid, "utf-8", "iso-8859-1",
+ NULL, NULL, NULL);
+ g_assert(utf);
+ retlist[i] = utf;
+ }
+ }
+
+ if (strlist) XFreeStringList(strlist);
+ if (return_single)
+ return retlist[0];
+ else
+ return retlist;
+}
+
+gboolean obt_prop_get32(Window win, Atom prop, Atom type, guint32 *ret)
+{
+ return get_prealloc(win, prop, type, 32, (guchar*)ret, 1);
+}
+
+gboolean obt_prop_get_array32(Window win, Atom prop, Atom type, guint32 **ret,
+ guint *nret)
+{
+ return get_all(win, prop, type, 32, (guchar**)ret, nret);
+}
+
+gboolean obt_prop_get_text(Window win, Atom prop, ObtPropTextType type,
+ gchar **ret_string)
+{
+ XTextProperty tprop;
+ gchar *str;
+ gboolean ret = FALSE;
+
+ if (get_text_property(win, prop, &tprop, type)) {
+ str = (gchar*)convert_text_property(&tprop, type, 1);
+
+ if (str) {
+ *ret_string = str;
+ ret = TRUE;
+ }
+ }
+ XFree(tprop.value);
+ return ret;
+}
+
+gboolean obt_prop_get_array_text(Window win, Atom prop,
+ ObtPropTextType type,
+ gchar ***ret_strings)
+{
+ XTextProperty tprop;
+ gchar **strs;
+ gboolean ret = FALSE;
+
+ if (get_text_property(win, prop, &tprop, type)) {
+ strs = (gchar**)convert_text_property(&tprop, type, -1);
+
+ if (strs) {
+ *ret_strings = strs;
+ ret = TRUE;
+ }
+ }
+ XFree(tprop.value);
+ return ret;
+}
+
+void obt_prop_set32(Window win, Atom prop, Atom type, gulong val)
+{
+ XChangeProperty(obt_display, win, prop, type, 32, PropModeReplace,
+ (guchar*)&val, 1);
+}
+
+void obt_prop_set_array32(Window win, Atom prop, Atom type, gulong *val,
+ guint num)
+{
+ XChangeProperty(obt_display, win, prop, type, 32, PropModeReplace,
+ (guchar*)val, num);
+}
+
+void obt_prop_set_text(Window win, Atom prop, const gchar *val)
+{
+ XChangeProperty(obt_display, win, prop, OBT_PROP_ATOM(UTF8_STRING), 8,
+ PropModeReplace, (const guchar*)val, strlen(val));
+}
+
+void obt_prop_set_array_text(Window win, Atom prop, const gchar *const *strs)
+{
+ GString *str;
+ gchar const *const *s;
+
+ str = g_string_sized_new(0);
+ for (s = strs; *s; ++s) {
+ str = g_string_append(str, *s);
+ str = g_string_append_c(str, '\0');
+ }
+ XChangeProperty(obt_display, win, prop, OBT_PROP_ATOM(UTF8_STRING), 8,
+ PropModeReplace, (guchar*)str->str, str->len);
+ g_string_free(str, TRUE);
+}
+
+void obt_prop_erase(Window win, Atom prop)
+{
+ XDeleteProperty(obt_display, win, prop);
+}
+
+void obt_prop_message(gint screen, Window about, Atom messagetype,
+ glong data0, glong data1, glong data2, glong data3,
+ glong data4, glong mask)
+{
+ obt_prop_message_to(obt_root(screen), about, messagetype,
+ data0, data1, data2, data3, data4, mask);
+}
+
+void obt_prop_message_to(Window to, Window about,
+ Atom messagetype,
+ glong data0, glong data1, glong data2, glong data3,
+ glong data4, glong mask)
+{
+ XEvent ce;
+ ce.xclient.type = ClientMessage;
+ ce.xclient.message_type = messagetype;
+ ce.xclient.display = obt_display;
+ ce.xclient.window = about;
+ ce.xclient.format = 32;
+ ce.xclient.data.l[0] = data0;
+ ce.xclient.data.l[1] = data1;
+ ce.xclient.data.l[2] = data2;
+ ce.xclient.data.l[3] = data3;
+ ce.xclient.data.l[4] = data4;
+ XSendEvent(obt_display, to, FALSE, mask, &ce);
+}
diff --git a/obt/prop.h b/obt/prop.h
new file mode 100644
index 0000000..b30232e
--- /dev/null
+++ b/obt/prop.h
@@ -0,0 +1,322 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/prop.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __obt_prop_h
+#define __obt_prop_h
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ /* types */
+ OBT_PROP_CARDINAL, /*!< The atom which represents the Cardinal data type */
+ OBT_PROP_WINDOW, /*!< The atom which represents window ids */
+ OBT_PROP_PIXMAP, /*!< The atom which represents pixmap ids */
+ OBT_PROP_ATOM, /*!< The atom which represents atom values */
+ OBT_PROP_STRING, /*!< The atom which represents latin1 strings */
+ OBT_PROP_COMPOUND_TEXT, /*!< The atom which represents locale-encoded
+ strings */
+ OBT_PROP_UTF8_STRING, /*!< The atom which represents utf8-encoded strings*/
+
+ /* selection stuff */
+ OBT_PROP_MANAGER,
+
+ /* window hints */
+ OBT_PROP_WM_COLORMAP_WINDOWS,
+ OBT_PROP_WM_PROTOCOLS,
+ OBT_PROP_WM_STATE,
+ OBT_PROP_WM_DELETE_WINDOW,
+ OBT_PROP_WM_TAKE_FOCUS,
+ OBT_PROP_WM_CHANGE_STATE,
+ OBT_PROP_WM_NAME,
+ OBT_PROP_WM_ICON_NAME,
+ OBT_PROP_WM_CLASS,
+ OBT_PROP_WM_WINDOW_ROLE,
+ OBT_PROP_WM_CLIENT_MACHINE,
+ OBT_PROP_WM_COMMAND,
+ OBT_PROP_WM_CLIENT_LEADER,
+ OBT_PROP_WM_TRANSIENT_FOR,
+ OBT_PROP_MOTIF_WM_HINTS,
+ OBT_PROP_MOTIF_WM_INFO,
+
+ /* SM atoms */
+ OBT_PROP_SM_CLIENT_ID,
+
+ /* NETWM atoms */
+
+ /* Atoms that are used inside messages - these don't go in net_supported */
+
+ OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPLEFT,
+ OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOP,
+ OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPRIGHT,
+ OBT_PROP_NET_WM_MOVERESIZE_SIZE_RIGHT,
+ OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT,
+ OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOM,
+ OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT,
+ OBT_PROP_NET_WM_MOVERESIZE_SIZE_LEFT,
+ OBT_PROP_NET_WM_MOVERESIZE_MOVE,
+ OBT_PROP_NET_WM_MOVERESIZE_SIZE_KEYBOARD,
+ OBT_PROP_NET_WM_MOVERESIZE_MOVE_KEYBOARD,
+ OBT_PROP_NET_WM_MOVERESIZE_CANCEL,
+
+ OBT_PROP_NET_WM_STATE_ADD,
+ OBT_PROP_NET_WM_STATE_REMOVE,
+ OBT_PROP_NET_WM_STATE_TOGGLE,
+
+ OBT_PROP_NET_WM_ORIENTATION_HORZ,
+ OBT_PROP_NET_WM_ORIENTATION_VERT,
+ OBT_PROP_NET_WM_TOPLEFT,
+ OBT_PROP_NET_WM_TOPRIGHT,
+ OBT_PROP_NET_WM_BOTTOMRIGHT,
+ OBT_PROP_NET_WM_BOTTOMLEFT,
+
+ OBT_PROP_NET_WM_WINDOW_TYPE_POPUP_MENU,
+
+ OBT_PROP_PRIVATE_PADDING1,
+ OBT_PROP_PRIVATE_PADDING2,
+ OBT_PROP_PRIVATE_PADDING3,
+ OBT_PROP_PRIVATE_PADDING4,
+ OBT_PROP_PRIVATE_PADDING5,
+ OBT_PROP_PRIVATE_PADDING6,
+ OBT_PROP_PRIVATE_PADDING7,
+ OBT_PROP_PRIVATE_PADDING8,
+ OBT_PROP_PRIVATE_PADDING9,
+ OBT_PROP_PRIVATE_PADDING10,
+ OBT_PROP_PRIVATE_PADDING11,
+ OBT_PROP_PRIVATE_PADDING12,
+
+ /* Everything below here must go in net_supported on the root window */
+
+ /* root window properties */
+ OBT_PROP_NET_SUPPORTED,
+ OBT_PROP_NET_CLIENT_LIST,
+ OBT_PROP_NET_CLIENT_LIST_STACKING,
+ OBT_PROP_NET_NUMBER_OF_DESKTOPS,
+ OBT_PROP_NET_DESKTOP_GEOMETRY,
+ OBT_PROP_NET_DESKTOP_VIEWPORT,
+ OBT_PROP_NET_CURRENT_DESKTOP,
+ OBT_PROP_NET_DESKTOP_NAMES,
+ OBT_PROP_NET_ACTIVE_WINDOW,
+/* Atom net_restack_window;*/
+ OBT_PROP_NET_WORKAREA,
+ OBT_PROP_NET_SUPPORTING_WM_CHECK,
+ OBT_PROP_NET_DESKTOP_LAYOUT,
+ OBT_PROP_NET_SHOWING_DESKTOP,
+
+ /* root window messages */
+ OBT_PROP_NET_CLOSE_WINDOW,
+ OBT_PROP_NET_WM_MOVERESIZE,
+ OBT_PROP_NET_MOVERESIZE_WINDOW,
+ OBT_PROP_NET_REQUEST_FRAME_EXTENTS,
+ OBT_PROP_NET_RESTACK_WINDOW,
+
+ /* helpful hints to apps that aren't used for anything */
+ OBT_PROP_NET_WM_FULL_PLACEMENT,
+
+ /* startup-notification extension */
+ OBT_PROP_NET_STARTUP_ID,
+
+ /* application window properties */
+ OBT_PROP_NET_WM_NAME,
+ OBT_PROP_NET_WM_VISIBLE_NAME,
+ OBT_PROP_NET_WM_ICON_NAME,
+ OBT_PROP_NET_WM_VISIBLE_ICON_NAME,
+ OBT_PROP_NET_WM_DESKTOP,
+ OBT_PROP_NET_WM_WINDOW_TYPE,
+ OBT_PROP_NET_WM_STATE,
+ OBT_PROP_NET_WM_STRUT,
+ OBT_PROP_NET_WM_STRUT_PARTIAL,
+ OBT_PROP_NET_WM_ICON,
+ OBT_PROP_NET_WM_ICON_GEOMETRY,
+ OBT_PROP_NET_WM_PID,
+ OBT_PROP_NET_WM_ALLOWED_ACTIONS,
+ OBT_PROP_NET_WM_WINDOW_OPACITY,
+ OBT_PROP_NET_WM_USER_TIME,
+/* OBT_PROP_NET_WM_USER_TIME_WINDOW, */
+ OBT_PROP_NET_FRAME_EXTENTS,
+
+ /* application protocols */
+ OBT_PROP_NET_WM_PING,
+#ifdef SYNC
+ OBT_PROP_NET_WM_SYNC_REQUEST,
+ OBT_PROP_NET_WM_SYNC_REQUEST_COUNTER,
+#endif
+
+ OBT_PROP_NET_WM_WINDOW_TYPE_DESKTOP,
+ OBT_PROP_NET_WM_WINDOW_TYPE_DOCK,
+ OBT_PROP_NET_WM_WINDOW_TYPE_TOOLBAR,
+ OBT_PROP_NET_WM_WINDOW_TYPE_MENU,
+ OBT_PROP_NET_WM_WINDOW_TYPE_UTILITY,
+ OBT_PROP_NET_WM_WINDOW_TYPE_SPLASH,
+ OBT_PROP_NET_WM_WINDOW_TYPE_DIALOG,
+ OBT_PROP_NET_WM_WINDOW_TYPE_NORMAL,
+
+ OBT_PROP_NET_WM_ACTION_MOVE,
+ OBT_PROP_NET_WM_ACTION_RESIZE,
+ OBT_PROP_NET_WM_ACTION_MINIMIZE,
+ OBT_PROP_NET_WM_ACTION_SHADE,
+/* OBT_PROP_NET_WM_ACTION_STICK,*/
+ OBT_PROP_NET_WM_ACTION_MAXIMIZE_HORZ,
+ OBT_PROP_NET_WM_ACTION_MAXIMIZE_VERT,
+ OBT_PROP_NET_WM_ACTION_FULLSCREEN,
+ OBT_PROP_NET_WM_ACTION_CHANGE_DESKTOP,
+ OBT_PROP_NET_WM_ACTION_CLOSE,
+ OBT_PROP_NET_WM_ACTION_ABOVE,
+ OBT_PROP_NET_WM_ACTION_BELOW,
+
+ OBT_PROP_NET_WM_STATE_MODAL,
+/* OBT_PROP_NET_WM_STATE_STICKY,*/
+ OBT_PROP_NET_WM_STATE_MAXIMIZED_VERT,
+ OBT_PROP_NET_WM_STATE_MAXIMIZED_HORZ,
+ OBT_PROP_NET_WM_STATE_SHADED,
+ OBT_PROP_NET_WM_STATE_SKIP_TASKBAR,
+ OBT_PROP_NET_WM_STATE_SKIP_PAGER,
+ OBT_PROP_NET_WM_STATE_HIDDEN,
+ OBT_PROP_NET_WM_STATE_FULLSCREEN,
+ OBT_PROP_NET_WM_STATE_ABOVE,
+ OBT_PROP_NET_WM_STATE_BELOW,
+ OBT_PROP_NET_WM_STATE_DEMANDS_ATTENTION,
+
+ /* KDE atoms */
+
+ OBT_PROP_KDE_WM_CHANGE_STATE,
+ OBT_PROP_KDE_NET_WM_FRAME_STRUT,
+ OBT_PROP_KDE_NET_WM_WINDOW_TYPE_OVERRIDE,
+
+/*
+ OBT_PROP_ROOTPMAPID,
+ OBT_PROP_ESETROOTID,
+*/
+
+ /* Openbox specific atoms */
+
+ OBT_PROP_OB_WM_ACTION_UNDECORATE,
+ OBT_PROP_OB_WM_STATE_UNDECORATED,
+ OBT_PROP_OPENBOX_PID, /* this is depreecated in favour of ob_control */
+ OBT_PROP_OB_THEME,
+ OBT_PROP_OB_CONFIG_FILE,
+ OBT_PROP_OB_CONTROL,
+ OBT_PROP_OB_VERSION,
+ OBT_PROP_OB_APP_ROLE,
+ OBT_PROP_OB_APP_TITLE,
+ OBT_PROP_OB_APP_NAME,
+ OBT_PROP_OB_APP_CLASS,
+ OBT_PROP_OB_APP_TYPE,
+
+ OBT_PROP_NUM_ATOMS
+} ObtPropAtom;
+
+Atom obt_prop_atom(ObtPropAtom a);
+
+typedef enum {
+ /*! STRING is latin1 encoded. It cannot contain control characters except
+ for tab and line-feed. */
+ OBT_PROP_TEXT_STRING = 1,
+ /*! STRING text restricted to characters in the X Portable Character
+ Set, which is a subset of latin1.
+ http://static.cray-cyber.org/Documentation/NEC_SX_R10_1/G1AE02E/CHAP1.HTML
+ */
+ OBT_PROP_TEXT_STRING_XPCS = 2,
+ /*! STRING text restricted to not allow any control characters to be
+ present. */
+ OBT_PROP_TEXT_STRING_NO_CC = 3,
+ /* COMPOUND_TEXT is encoded in the current locale setting. */
+ OBT_PROP_TEXT_COMPOUND_TEXT = 4,
+ /* UTF8_STRING is encoded as utf-8. */
+ OBT_PROP_TEXT_UTF8_STRING = 5,
+} ObtPropTextType;
+
+gboolean obt_prop_get32(Window win, Atom prop, Atom type, guint32 *ret);
+gboolean obt_prop_get_array32(Window win, Atom prop, Atom type, guint32 **ret,
+ guint *nret);
+
+gboolean obt_prop_get_text(Window win, Atom prop, ObtPropTextType type,
+ gchar **ret);
+gboolean obt_prop_get_array_text(Window win, Atom prop,
+ ObtPropTextType type,
+ gchar ***ret);
+
+void obt_prop_set32(Window win, Atom prop, Atom type, gulong val);
+void obt_prop_set_array32(Window win, Atom prop, Atom type, gulong *val,
+ guint num);
+void obt_prop_set_text(Window win, Atom prop, const gchar *str);
+void obt_prop_set_array_text(Window win, Atom prop, const gchar *const *strs);
+
+void obt_prop_erase(Window win, Atom prop);
+
+void obt_prop_message(gint screen, Window about, Atom messagetype,
+ glong data0, glong data1, glong data2, glong data3,
+ glong data4, glong mask);
+void obt_prop_message_to(Window to, Window about, Atom messagetype,
+ glong data0, glong data1, glong data2, glong data3,
+ glong data4, glong mask);
+
+#define OBT_PROP_ATOM(prop) obt_prop_atom(OBT_PROP_##prop)
+
+#define OBT_PROP_GET32(win, prop, type, ret) \
+ (obt_prop_get32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), ret))
+#define OBT_PROP_GETA32(win, prop, type, ret, nret) \
+ (obt_prop_get_array32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), \
+ ret, nret))
+#define OBT_PROP_GETS(win, prop, ret) \
+ (obt_prop_get_text(win, OBT_PROP_ATOM(prop), 0, ret))
+#define OBT_PROP_GETSS(win, prop, ret) \
+ (obt_prop_get_array_text(win, OBT_PROP_ATOM(prop), 0, ret))
+
+#define OBT_PROP_GETS_TYPE(win, prop, type, ret) \
+ (obt_prop_get_text(win, OBT_PROP_ATOM(prop), OBT_PROP_TEXT_##type, ret))
+#define OBT_PROP_GETSS_TYPE(win, prop, type, ret) \
+ (obt_prop_get_array_text(win, OBT_PROP_ATOM(prop), \
+ OBT_PROP_TEXT_##type, ret))
+
+#define OBT_PROP_GETS_UTF8(win, prop, ret) \
+ OBT_PROP_GETS_TYPE(win, prop, UTF8_STRING, ret)
+#define OBT_PROP_GETSS_UTF8(win, prop, ret) \
+ OBT_PROP_GETSS_TYPE(win, prop, UTF8_STRING, ret)
+#define OBT_PROP_GETS_XPCS(win, prop, ret) \
+ OBT_PROP_GETS_TYPE(win, prop, STRING_XPCS, ret)
+
+#define OBT_PROP_SET32(win, prop, type, val) \
+ (obt_prop_set32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), val))
+#define OBT_PROP_SETA32(win, prop, type, val, num) \
+ (obt_prop_set_array32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), \
+ val, num))
+#define OBT_PROP_SETS(win, prop, val) \
+ (obt_prop_set_text(win, OBT_PROP_ATOM(prop), val))
+#define OBT_PROP_SETSS(win, prop, strs) \
+ (obt_prop_set_array_text(win, OBT_PROP_ATOM(prop), strs))
+
+#define OBT_PROP_ERASE(win, prop) (obt_prop_erase(win, OBT_PROP_ATOM(prop)))
+
+#define OBT_PROP_MSG(screen, about, msgtype, data0, data1, data2, data3, \
+ data4) \
+ (obt_prop_message(screen, about, OBT_PROP_ATOM(msgtype), \
+ data0, data1, data2, data3, data4, \
+ SubstructureNotifyMask | SubstructureRedirectMask))
+
+#define OBT_PROP_MSG_TO(to, about, msgtype, data0, data1, data2, data3, \
+ data4, mask) \
+ (obt_prop_message_to(to, about, OBT_PROP_ATOM(msgtype), \
+ data0, data1, data2, data3, data4, mask))
+
+G_END_DECLS
+
+#endif /* __obt_prop_h */
diff --git a/obt/signal.c b/obt/signal.c
new file mode 100644
index 0000000..0e483b7
--- /dev/null
+++ b/obt/signal.c
@@ -0,0 +1,290 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/signal.c for the Openbox window manager
+ Copyright (c) 2010 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "signal.h"
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+typedef struct _ObtSignalCallback ObtSignalCallback;
+
+struct _ObtSignalCallback
+{
+ ObtSignalHandler func;
+ gpointer data;
+};
+
+static gboolean signal_prepare(GSource *source, gint *timeout);
+static gboolean signal_check(GSource *source);
+static gboolean signal_occurred(GSource *source, GSourceFunc callback,
+ gpointer data);
+static void sighandler(gint sig);
+
+/* this should be more than the number of possible signals on any
+ architecture... */
+#define NUM_SIGNALS 99
+
+/* a set of all possible signals */
+static sigset_t all_signals_set;
+
+/* keep track of what signals have a signal handler installed, and remember
+ the action we replaced when installing it for when we clean up */
+static struct {
+ guint installed; /* a ref count */
+ struct sigaction oldact;
+} all_signals[NUM_SIGNALS];
+
+/* signals which cause a core dump, these can't be used for callbacks */
+static const gint core_signals[] =
+{
+ SIGABRT,
+ SIGSEGV,
+ SIGFPE,
+ SIGILL,
+ SIGQUIT,
+ SIGTRAP,
+ SIGSYS,
+ SIGBUS,
+ SIGXCPU,
+ SIGXFSZ
+};
+#define NUM_CORE_SIGNALS (gint)(sizeof(core_signals) / sizeof(core_signals[0]))
+
+static GSourceFuncs source_funcs = {
+ signal_prepare,
+ signal_check,
+ signal_occurred,
+ NULL
+};
+static GSource *gsource = NULL;
+static guint listeners = 0; /* a ref count for the signal listener */
+static gboolean signal_fired;
+guint signals_fired[NUM_SIGNALS];
+GSList *callbacks[NUM_SIGNALS];
+
+void obt_signal_listen(void)
+{
+ if (!listeners) {
+ guint i;
+ struct sigaction action;
+ sigset_t sigset;
+
+ /* initialize the all_signals_set */
+ sigfillset(&all_signals_set);
+
+ sigemptyset(&sigset);
+ action.sa_handler = sighandler;
+ action.sa_mask = sigset;
+ action.sa_flags = SA_NOCLDSTOP;
+
+ /* always grab all the signals that cause core dumps */
+ for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
+ /* SIGABRT is curiously not grabbed here!! that's because when we
+ get one of the core_signals, we use abort() to dump the core.
+ And having the abort() only go back to our signal handler again
+ is less than optimal */
+ if (core_signals[i] != SIGABRT) {
+ sigaction(core_signals[i], &action,
+ &all_signals[core_signals[i]].oldact);
+ all_signals[core_signals[i]].installed++;
+ }
+ }
+
+ gsource = g_source_new(&source_funcs, sizeof(GSource));
+ g_source_set_priority(gsource, G_PRIORITY_HIGH);
+
+ g_source_attach(gsource, NULL);
+ }
+
+ ++listeners;
+}
+
+void obt_signal_stop(void)
+{
+ --listeners;
+
+ if (!listeners) {
+ gint i;
+ GSList *it, *next;
+
+ g_source_unref(gsource);
+ gsource = NULL;
+
+ /* remove user defined signal handlers */
+ for (i = 0; i < NUM_SIGNALS; ++i)
+ for (it = callbacks[i]; it; it = next) {
+ ObtSignalCallback *cb = it->data;
+ next = g_slist_next(it);
+ obt_signal_remove_callback(i, cb->func);
+ }
+
+ /* release all the signals that cause core dumps */
+ for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
+ if (all_signals[core_signals[i]].installed) {
+ sigaction(core_signals[i],
+ &all_signals[core_signals[i]].oldact, NULL);
+ all_signals[core_signals[i]].installed--;
+ }
+ }
+
+#ifdef DEBUG
+ for (i = 0; i < NUM_SIGNALS; ++i)
+ g_assert(all_signals[i].installed == 0);
+#endif
+ }
+}
+
+void obt_signal_add_callback(gint sig, ObtSignalHandler func, gpointer data)
+{
+ ObtSignalCallback *cb;
+ gint i;
+
+ g_return_if_fail(func != NULL);
+ g_return_if_fail(sig >= 0 && sig <= NUM_SIGNALS);
+ for (i = 0; i < NUM_CORE_SIGNALS; ++i)
+ g_return_if_fail(sig != core_signals[i]);
+
+ cb = g_slice_new(ObtSignalCallback);
+ cb->func = func;
+ cb->data = data;
+ callbacks[sig] = g_slist_prepend(callbacks[sig], cb);
+
+ /* install the signal handler */
+ if (!all_signals[sig].installed) {
+ struct sigaction action;
+ sigset_t sigset;
+
+ sigemptyset(&sigset);
+ action.sa_handler = sighandler;
+ action.sa_mask = sigset;
+ action.sa_flags = SA_NOCLDSTOP;
+
+ sigaction(sig, &action, &all_signals[sig].oldact);
+ }
+
+ all_signals[sig].installed++;
+}
+
+void obt_signal_remove_callback(gint sig, ObtSignalHandler func)
+{
+ GSList *it;
+ gint i;
+
+ g_return_if_fail(func != NULL);
+ g_return_if_fail(sig >= 0 && sig <= NUM_SIGNALS);
+ for (i = 0; i < NUM_CORE_SIGNALS; ++i)
+ g_return_if_fail(sig != core_signals[i]);
+
+ for (it = callbacks[sig]; it; it = g_slist_next(it)) {
+ ObtSignalCallback *cb = it->data;
+ if (cb->func == func) {
+ g_assert(all_signals[sig].installed > 0);
+
+ callbacks[sig] = g_slist_delete_link(callbacks[sig], it);
+ g_slice_free(ObtSignalCallback, cb);
+
+ /* uninstall the signal handler */
+ all_signals[sig].installed--;
+ if (!all_signals[sig].installed)
+ sigaction(sig, &all_signals[sig].oldact, NULL);
+ break;
+ }
+ }
+}
+
+static gboolean signal_prepare(GSource *source, gint *timeout)
+{
+ *timeout = -1;
+ return signal_fired;
+}
+
+static gboolean signal_check(GSource *source)
+{
+ return signal_fired;
+}
+
+static gboolean signal_occurred(GSource *source, GSourceFunc callback,
+ gpointer data)
+{
+ guint i;
+ sigset_t oldset;
+ guint fired[NUM_SIGNALS];
+
+ /* block signals so that we can do this without the data changing
+ on us */
+ sigprocmask(SIG_SETMASK, &all_signals_set, &oldset);
+
+ /* make a copy of the signals that fired */
+ for (i = 0; i < NUM_SIGNALS; ++i) {
+ fired[i] = signals_fired[i];
+ signals_fired[i] = 0;
+ }
+ signal_fired = FALSE;
+
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+
+ /* call the signal callbacks for the signals */
+ for (i = 0; i < NUM_SIGNALS; ++i) {
+ while (fired[i]) {
+ GSList *it;
+ for (it = callbacks[i]; it; it = g_slist_next(it)) {
+ const ObtSignalCallback *cb = it->data;
+ cb->func(i, cb->data);
+ }
+ --fired[i];
+ }
+ }
+
+ return TRUE; /* repeat */
+}
+
+static void sighandler(gint sig)
+{
+ guint i;
+
+ g_return_if_fail(sig < NUM_SIGNALS);
+
+ for (i = 0; i < NUM_CORE_SIGNALS; ++i)
+ if (sig == core_signals[i]) {
+ /* XXX special case for signals that default to core dump.
+ but throw some helpful output here... */
+
+ fprintf(stderr, "How are you gentlemen? All your base are"
+ " belong to us. (Openbox received signal %d)\n", sig);
+
+ /* die with a core dump */
+ abort();
+ }
+
+ signal_fired = TRUE;
+ ++signals_fired[sig];
+
+ /* i don't think we want to modify the GMainContext inside a signal
+ handler, so use a GSource instead of an idle func to call back
+ to the application */
+}
diff --git a/obt/signal.h b/obt/signal.h
new file mode 100644
index 0000000..ec712b2
--- /dev/null
+++ b/obt/signal.h
@@ -0,0 +1,46 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/signal.h for the Openbox window manager
+ Copyright (c) 2010 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __obt_signal_h
+#define __obt_signal_h
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef void (*ObtSignalHandler)(gint signal, gpointer data);
+
+/*! Listen for signals and report them through the default GMainContext within
+ main program thread (except signals that require immediate exit).
+ The app should not set its own signal handler function or it will interfere
+ with this one. */
+void obt_signal_listen(void);
+/*! Stop listening to signals and clean up */
+void obt_signal_stop(void);
+
+/*! Adds a signal handler for a signal. The callback function @func will be
+ called when the signal @sig is fired. @sig must not be a signal that
+ would cause the core to dump as these are handled internally.
+ */
+void obt_signal_add_callback(gint sig, ObtSignalHandler func, gpointer data);
+/*! Removes the most recently added callback with the given function. */
+void obt_signal_remove_callback(gint sig, ObtSignalHandler func);
+
+G_END_DECLS
+
+#endif
diff --git a/obt/tests/bstest.c b/obt/tests/bstest.c
new file mode 100755
index 0000000..7581855
--- /dev/null
+++ b/obt/tests/bstest.c
@@ -0,0 +1,58 @@
+#/*
+#!/bin/sh
+#*/
+#if 0
+gcc -O0 -o ./bstest `pkg-config --cflags --libs obt-3.5` bstest.c && \
+./bstest
+exit
+#endif
+
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ bstest.c for the Openbox window manager
+ Copyright (c) 2010 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "../bsearch.h"
+#include <stdio.h>
+
+int main() {
+ int ar[] = {
+ 2, 4, 5, 7, 12, 34, 45, 56, 57, 67, 67, 68, 68, 69, 70, 71, 89, 100 };
+ int n = sizeof(ar)/sizeof(ar[0]);
+ BSEARCH_SETUP(int);
+ BSEARCH(int, ar, 0, n, 1);
+ g_assert(!!BSEARCH_FOUND() == FALSE);
+ BSEARCH(int, ar, 0, n, 0);
+ g_assert(!!BSEARCH_FOUND() == FALSE);
+ BSEARCH(int, ar, 0, n, 2);
+ g_assert(!!BSEARCH_FOUND() == TRUE);
+ g_assert(BSEARCH_AT() == 0);
+ BSEARCH(int, ar, 0, n, 58);
+ g_assert(!!BSEARCH_FOUND() == FALSE);
+ BSEARCH(int, ar, 0, n, 57);
+ g_assert(!!BSEARCH_FOUND() == TRUE);
+ g_assert(BSEARCH_AT() == 8);
+ BSEARCH(int, ar, 0, n, 55);
+ g_assert(!!BSEARCH_FOUND() == FALSE);
+ BSEARCH(int, ar, 0, n, 99);
+ g_assert(!!BSEARCH_FOUND() == FALSE);
+ BSEARCH(int, ar, 0, n, 100);
+ g_assert(!!BSEARCH_FOUND() == TRUE);
+ g_assert(BSEARCH_AT() == 17);
+ BSEARCH(int, ar, 0, n, 101);
+ g_assert(!!BSEARCH_FOUND() == FALSE);
+ g_print("ok\n");
+}
diff --git a/obt/tests/ddtest.c b/obt/tests/ddtest.c
new file mode 100755
index 0000000..69a9e1c
--- /dev/null
+++ b/obt/tests/ddtest.c
@@ -0,0 +1,61 @@
+#/*
+#!/bin/sh
+#*/
+#if 0
+gcc -O0 -o ./ddtest `pkg-config --cflags --libs obt-3.5` ddtest.c && \
+./ddtest
+exit
+#endif
+
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ ddtest.c for the Openbox window manager
+ Copyright (c) 2010 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "obt/paths.h"
+#include "obt/link.h"
+#include <glib.h>
+
+gint main(int argc, char **argv)
+{
+ ObtPaths *obtpaths;
+ ObtLink *dd;
+ gchar *id;
+
+ if (argc < 2) {
+ g_print("pass path to .desktop\n");
+ return 1;
+ }
+
+ obtpaths = obt_paths_new();
+ dd = obt_link_from_ddfile(argv[1], obtpaths, "et", NULL, NULL);
+ obt_paths_unref(obtpaths);
+ if (dd) {
+ g_print("Success\n");
+ {
+ gulong i, n;
+ const GQuark *c = obt_link_app_categories(dd, &n);
+ for (i = 0; i < n; ++i)
+ g_print("Category: %s\n",
+ g_quark_to_string(c[i]));
+ }
+ obt_link_unref(dd);
+ }
+ return 0;
+}
diff --git a/obt/tests/ddtest.desktop b/obt/tests/ddtest.desktop
new file mode 100644
index 0000000..16d76aa
--- /dev/null
+++ b/obt/tests/ddtest.desktop
@@ -0,0 +1,15 @@
+[Desktop Entry]
+test=
+test2
+foo =
+#hi
+gewh= yuhself
+a-r950 = tek;la; fi
+hi=bye
+
+you=yeh
+hi=double
+Type=Application
+Exec=foo
+Name=myname
+Categories=one;two;;three
diff --git a/obt/tests/linktest.c b/obt/tests/linktest.c
new file mode 100755
index 0000000..022ba35
--- /dev/null
+++ b/obt/tests/linktest.c
@@ -0,0 +1,48 @@
+#/*
+#!/bin/sh
+#*/
+#if 0
+gcc -O0 -o ./linktest `pkg-config --cflags --libs obt-3.5` linktest.c && \
+./linktest
+exit
+#endif
+
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ linktest.c for the Openbox window manager
+ Copyright (c) 2010 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "obt/linkbase.h"
+#include "obt/paths.h"
+#include <glib.h>
+#include <locale.h>
+
+gint main()
+{
+ ObtLinkBase *base;
+ ObtPaths *paths;
+ GMainLoop *loop;
+
+ paths = obt_paths_new();
+ base = obt_linkbase_new(paths, setlocale(LC_MESSAGES, ""));
+ printf("done\n");
+ return 0;
+
+ loop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(loop);
+
+ return 0;
+}
diff --git a/obt/tests/watchtest.c b/obt/tests/watchtest.c
new file mode 100755
index 0000000..9ebdbae
--- /dev/null
+++ b/obt/tests/watchtest.c
@@ -0,0 +1,50 @@
+#/*
+#!/bin/sh
+#*/
+#if 0
+gcc -O0 -o ./watchtest `pkg-config --cflags --libs obt-3.5` watchtest.c && \
+./watchtest
+exit
+#endif
+
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ watchtest.c for the Openbox window manager
+ Copyright (c) 2010 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "obt/watch.h"
+#include <glib.h>
+
+void func(ObtWatch *w, const gchar *base_path,
+ const gchar *subpath, ObtWatchNotifyType type,
+ gpointer data)
+{
+ g_print("base path: %s subpath: %s type=%d\n", base_path, subpath, type);
+}
+
+gint main()
+{
+ ObtWatch *watch;
+ GMainLoop *loop;
+
+ watch = obt_watch_new();
+ obt_watch_add(watch, "/tmp/a", FALSE, func, NULL);
+
+ loop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(loop);
+
+ return 0;
+}
diff --git a/obt/util.h b/obt/util.h
new file mode 100644
index 0000000..ff44d36
--- /dev/null
+++ b/obt/util.h
@@ -0,0 +1,37 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/util.h for the Openbox window manager
+ Copyright (c) 2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __obt_util_h
+#define __obt_util_h
+
+#include <glib.h>
+
+#ifdef HAVE_STRING_H
+# include <string.h> /* for memset() */
+#endif
+
+G_BEGIN_DECLS
+
+/* Util funcs */
+#define obt_free g_free
+#define obt_free0(p, type, num) memset((p), 0, sizeof(type) * (num)), g_free(p)
+
+G_END_DECLS
+
+
+#endif /*__obt_util_h*/
diff --git a/obt/version.h.in b/obt/version.h.in
new file mode 100644
index 0000000..8adfcf8
--- /dev/null
+++ b/obt/version.h.in
@@ -0,0 +1,15 @@
+#ifndef obt__version_h
+#define obt__version_h
+
+#define OBT_MAJOR_VERSION @OBT_MAJOR_VERSION@
+#define OBT_MINOR_VERSION @OBT_MINOR_VERSION@
+#define OBT_MICRO_VERSION @OBT_MICRO_VERSION@
+#define OBT_VERSION OBT_MAJOR_VERSION.OBT_MINOR_VERSION.OBT_MICRO_VERSION
+
+#define OBT_CHECK_VERSION(major,minor,micro) \
+ (OBT_MAJOR_VERSION > (major) || \
+ (OBT_MAJOR_VERSION == (major) && OBT_MINOR_VERSION > (minor)) || \
+ (OBT_MAJOR_VERSION == (major) && OBT_MINOR_VERSION == (minor) && \
+ OBT_MICRO_VERSION >= (micro)))
+
+#endif
diff --git a/obt/xml.c b/obt/xml.c
new file mode 100644
index 0000000..c872912
--- /dev/null
+++ b/obt/xml.c
@@ -0,0 +1,436 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/xml.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "obt/xml.h"
+#include "obt/paths.h"
+
+#include <libxml/xinclude.h>
+#include <glib.h>
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+struct Callback {
+ gchar *tag;
+ ObtXmlCallback func;
+ gpointer data;
+};
+
+struct _ObtXmlInst {
+ gint ref;
+ ObtPaths *xdg_paths;
+ GHashTable *callbacks;
+ xmlDocPtr doc;
+ xmlNodePtr root;
+ gchar *path;
+};
+
+static void destfunc(struct Callback *c)
+{
+ g_free(c->tag);
+ g_slice_free(struct Callback, c);
+}
+
+ObtXmlInst* obt_xml_instance_new(void)
+{
+ ObtXmlInst *i = g_slice_new(ObtXmlInst);
+ i->ref = 1;
+ i->xdg_paths = obt_paths_new();
+ i->callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+ (GDestroyNotify)destfunc);
+ i->doc = NULL;
+ i->root = NULL;
+ i->path = NULL;
+ return i;
+}
+
+void obt_xml_instance_ref(ObtXmlInst *i)
+{
+ ++i->ref;
+}
+
+void obt_xml_instance_unref(ObtXmlInst *i)
+{
+ if (i && --i->ref == 0) {
+ obt_paths_unref(i->xdg_paths);
+ g_hash_table_destroy(i->callbacks);
+ g_slice_free(ObtXmlInst, i);
+ }
+}
+
+xmlDocPtr obt_xml_doc(ObtXmlInst *i)
+{
+ g_assert(i->doc); /* a doc is open? */
+ return i->doc;
+}
+
+xmlNodePtr obt_xml_root(ObtXmlInst *i)
+{
+ g_assert(i->doc); /* a doc is open? */
+ return i->root;
+}
+
+void obt_xml_register(ObtXmlInst *i, const gchar *tag,
+ ObtXmlCallback func, gpointer data)
+{
+ struct Callback *c;
+
+ if (g_hash_table_lookup(i->callbacks, tag)) {
+ g_error("Tag '%s' already registered", tag);
+ return;
+ }
+
+ c = g_slice_new(struct Callback);
+ c->tag = g_strdup(tag);
+ c->func = func;
+ c->data = data;
+ g_hash_table_insert(i->callbacks, c->tag, c);
+}
+
+static gboolean load_file(ObtXmlInst *i,
+ const gchar *domain,
+ const gchar *filename,
+ const gchar *root_node,
+ GSList *paths)
+{
+ GSList *it;
+ gboolean r = FALSE;
+
+ g_assert(i->doc == NULL); /* another doc isn't open already? */
+
+ for (it = paths; !r && it; it = g_slist_next(it)) {
+ gchar *path;
+ struct stat s;
+
+ if (!domain && !filename) /* given a full path to the file */
+ path = g_strdup(it->data);
+ else
+ path = g_build_filename(it->data, domain, filename, NULL);
+
+ if (stat(path, &s) >= 0) {
+ /* XML_PARSE_BLANKS is needed apparently, or the tree can end up
+ with extra nodes in it. */
+ i->doc = xmlReadFile(path, NULL, (XML_PARSE_NOBLANKS |
+ XML_PARSE_RECOVER));
+ xmlXIncludeProcessFlags(i->doc, (XML_PARSE_NOBLANKS |
+ XML_PARSE_RECOVER));
+ if (i->doc) {
+ i->root = xmlDocGetRootElement(i->doc);
+ if (!i->root) {
+ xmlFreeDoc(i->doc);
+ i->doc = NULL;
+ g_message("%s is an empty XML document", path);
+ }
+ else if (xmlStrcmp(i->root->name,
+ (const xmlChar*)root_node)) {
+ xmlFreeDoc(i->doc);
+ i->doc = NULL;
+ i->root = NULL;
+ g_message("XML document %s is of wrong type. Root "
+ "node is not '%s'", path, root_node);
+ }
+ else {
+ i->path = g_strdup(path);
+ r = TRUE; /* ok! */
+ }
+ }
+ }
+
+ g_free(path);
+ }
+
+ return r;
+}
+
+gboolean obt_xml_load_file(ObtXmlInst *i,
+ const gchar *path,
+ const gchar *root_node)
+{
+ GSList *paths;
+ gboolean r;
+
+ paths = g_slist_append(NULL, g_strdup(path));
+
+ r = load_file(i, NULL, NULL, root_node, paths);
+
+ while (paths) {
+ g_free(paths->data);
+ paths = g_slist_delete_link(paths, paths);
+ }
+ return r;
+}
+
+gboolean obt_xml_load_config_file(ObtXmlInst *i,
+ const gchar *domain,
+ const gchar *filename,
+ const gchar *root_node)
+{
+ GSList *it, *paths = NULL;
+ gboolean r;
+
+ for (it = obt_paths_config_dirs(i->xdg_paths); it; it = g_slist_next(it))
+ paths = g_slist_append(paths, g_strdup(it->data));
+
+ r = load_file(i, domain, filename, root_node, paths);
+
+ while (paths) {
+ g_free(paths->data);
+ paths = g_slist_delete_link(paths, paths);
+ }
+ return r;
+}
+
+gboolean obt_xml_load_data_file(ObtXmlInst *i,
+ const gchar *domain,
+ const gchar *filename,
+ const gchar *root_node)
+{
+ GSList *it, *paths = NULL;
+ gboolean r;
+
+ for (it = obt_paths_data_dirs(i->xdg_paths); it; it = g_slist_next(it))
+ paths = g_slist_append(paths, g_strdup(it->data));
+
+ r = load_file(i, domain, filename, root_node, paths);
+
+ while (paths) {
+ g_free(paths->data);
+ paths = g_slist_delete_link(paths, paths);
+ }
+ return r;
+}
+
+gboolean obt_xml_load_theme_file(ObtXmlInst *i,
+ const gchar *theme,
+ const gchar *domain,
+ const gchar *filename,
+ const gchar *root_node)
+{
+ GSList *it, *paths = NULL;
+ gboolean r;
+
+ /* use ~/.themes for backwards compatibility */
+ paths = g_slist_append
+ (paths, g_build_filename(g_get_home_dir(), ".themes", theme, NULL));
+
+ for (it = obt_paths_data_dirs(i->xdg_paths); it; it = g_slist_next(it))
+ paths = g_slist_append
+ (paths, g_build_filename(it->data, "themes", theme, NULL));
+
+ r = load_file(i, domain, filename, root_node, paths);
+
+ while (paths) {
+ g_free(paths->data);
+ paths = g_slist_delete_link(paths, paths);
+ }
+ return r;
+}
+
+
+gboolean obt_xml_load_mem(ObtXmlInst *i,
+ gpointer data, guint len, const gchar *root_node)
+{
+ gboolean r = FALSE;
+
+ g_assert(i->doc == NULL); /* another doc isn't open already? */
+
+ i->doc = xmlParseMemory(data, len);
+ if (i) {
+ i->root = xmlDocGetRootElement(i->doc);
+ if (!i->root) {
+ xmlFreeDoc(i->doc);
+ i->doc = NULL;
+ g_message("Given memory is an empty document");
+ }
+ else if (xmlStrcmp(i->root->name, (const xmlChar*)root_node)) {
+ xmlFreeDoc(i->doc);
+ i->doc = NULL;
+ i->root = NULL;
+ g_message("XML Document in given memory is of wrong "
+ "type. Root node is not '%s'\n", root_node);
+ }
+ else
+ r = TRUE; /* ok ! */
+ }
+ return r;
+}
+
+gboolean obt_xml_save_file(ObtXmlInst *inst,
+ const gchar *path,
+ gboolean pretty)
+{
+ return xmlSaveFormatFile(path, inst->doc, pretty) != -1;
+}
+
+void obt_xml_close(ObtXmlInst *i)
+{
+ if (i && i->doc) {
+ xmlFreeDoc(i->doc);
+ g_free(i->path);
+ i->doc = NULL;
+ i->root = NULL;
+ i->path = NULL;
+ }
+}
+
+void obt_xml_tree(ObtXmlInst *i, xmlNodePtr node)
+{
+ g_assert(i->doc); /* a doc is open? */
+
+ while (node) {
+ if (node->name) {
+ struct Callback *c = g_hash_table_lookup(i->callbacks, node->name);
+ if (c) c->func(node, c->data);
+ }
+ node = node->next;
+ }
+}
+
+void obt_xml_tree_from_root(ObtXmlInst *i)
+{
+ obt_xml_tree(i, i->root->children);
+}
+
+gchar *obt_xml_node_string(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;
+}
+
+gint obt_xml_node_int(xmlNodePtr node)
+{
+ xmlChar *c = xmlNodeGetContent(node);
+ gint i;
+ if (c) g_strstrip((char*)c); /* strip leading/trailing whitespace */
+ i = c ? atoi((gchar*)c) : 0;
+ xmlFree(c);
+ return i;
+}
+
+gboolean obt_xml_node_bool(xmlNodePtr node)
+{
+ xmlChar *c = xmlNodeGetContent(node);
+ gboolean b = FALSE;
+ if (c) g_strstrip((char*)c); /* strip leading/trailing whitespace */
+ if (c && !xmlStrcasecmp(c, (const xmlChar*) "true"))
+ b = TRUE;
+ else if (c && !xmlStrcasecmp(c, (const xmlChar*) "yes"))
+ b = TRUE;
+ else if (c && !xmlStrcasecmp(c, (const xmlChar*) "on"))
+ b = TRUE;
+ xmlFree(c);
+ return b;
+}
+
+gboolean obt_xml_node_contains(xmlNodePtr node, const gchar *val)
+{
+ xmlChar *c = xmlNodeGetContent(node);
+ gboolean r;
+ if (c) g_strstrip((char*)c); /* strip leading/trailing whitespace */
+ r = !xmlStrcasecmp(c, (const xmlChar*) val);
+ xmlFree(c);
+ return r;
+}
+
+xmlNodePtr obt_xml_find_node(xmlNodePtr node, const gchar *tag)
+{
+ while (node) {
+ if (!xmlStrcmp(node->name, (const xmlChar*) tag))
+ return node;
+ node = node->next;
+ }
+ return NULL;
+}
+
+gboolean obt_xml_attr_bool(xmlNodePtr node, const gchar *name,
+ gboolean *value)
+{
+ xmlChar *c = xmlGetProp(node, (const xmlChar*) name);
+ gboolean r = FALSE;
+ if (c) {
+ g_strstrip((char*)c); /* strip leading/trailing whitespace */
+ if (!xmlStrcasecmp(c, (const xmlChar*) "true"))
+ *value = TRUE, r = TRUE;
+ else if (!xmlStrcasecmp(c, (const xmlChar*) "yes"))
+ *value = TRUE, r = TRUE;
+ else if (!xmlStrcasecmp(c, (const xmlChar*) "on"))
+ *value = TRUE, r = TRUE;
+ else if (!xmlStrcasecmp(c, (const xmlChar*) "false"))
+ *value = FALSE, r = TRUE;
+ else if (!xmlStrcasecmp(c, (const xmlChar*) "no"))
+ *value = FALSE, r = TRUE;
+ else if (!xmlStrcasecmp(c, (const xmlChar*) "off"))
+ *value = FALSE, r = TRUE;
+ }
+ xmlFree(c);
+ return r;
+}
+
+gboolean obt_xml_attr_int(xmlNodePtr node, const gchar *name, gint *value)
+{
+ xmlChar *c = xmlGetProp(node, (const xmlChar*) name);
+ gboolean r = FALSE;
+ if (c) {
+ g_strstrip((char*)c); /* strip leading/trailing whitespace */
+ *value = atoi((gchar*)c);
+ r = TRUE;
+ }
+ xmlFree(c);
+ return r;
+}
+
+gboolean obt_xml_attr_string(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;
+ }
+ xmlFree(c);
+ return r;
+}
+
+gboolean obt_xml_attr_contains(xmlNodePtr node, const gchar *name,
+ const gchar *val)
+{
+ xmlChar *c = xmlGetProp(node, (const xmlChar*) name);
+ gboolean r = FALSE;
+ if (c) {
+ g_strstrip((char*)c); /* strip leading/trailing whitespace */
+ r = !xmlStrcasecmp(c, (const xmlChar*) val);
+ }
+ xmlFree(c);
+ return r;
+}
diff --git a/obt/xml.h b/obt/xml.h
new file mode 100644
index 0000000..ac2dc57
--- /dev/null
+++ b/obt/xml.h
@@ -0,0 +1,89 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/xml.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __obt_xml_h
+#define __obt_xml_h
+
+#include <libxml/parser.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ObtXmlInst ObtXmlInst;
+
+typedef void (*ObtXmlCallback)(xmlNodePtr node, gpointer data);
+
+ObtXmlInst* obt_xml_instance_new(void);
+void obt_xml_instance_ref(ObtXmlInst *inst);
+void obt_xml_instance_unref(ObtXmlInst *inst);
+
+gboolean obt_xml_load_file(ObtXmlInst *inst,
+ const gchar *path,
+ const gchar *root_node);
+gboolean obt_xml_load_config_file(ObtXmlInst *inst,
+ const gchar *domain,
+ const gchar *filename,
+ const gchar *root_node);
+gboolean obt_xml_load_data_file(ObtXmlInst *inst,
+ const gchar *domain,
+ const gchar *filename,
+ const gchar *root_node);
+gboolean obt_xml_load_theme_file(ObtXmlInst *inst,
+ const gchar *theme,
+ const gchar *domain,
+ const gchar *filename,
+ const gchar *root_node);
+gboolean obt_xml_load_mem(ObtXmlInst *inst,
+ gpointer data, guint len, const gchar *root_node);
+
+gboolean obt_xml_save_file(ObtXmlInst *inst,
+ const gchar *path,
+ gboolean pretty);
+
+xmlDocPtr obt_xml_doc(ObtXmlInst *inst);
+xmlNodePtr obt_xml_root(ObtXmlInst *inst);
+
+void obt_xml_close(ObtXmlInst *inst);
+
+void obt_xml_register(ObtXmlInst *inst, const gchar *tag,
+ ObtXmlCallback func, gpointer data);
+void obt_xml_tree(ObtXmlInst *i, xmlNodePtr node);
+void obt_xml_tree_from_root(ObtXmlInst *i);
+
+
+/* helpers */
+
+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 (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 (xmlNodePtr node, const gchar *name,
+ gchar **value);
+gboolean obt_xml_attr_int (xmlNodePtr node, const gchar *name,
+ gint *value);
+gboolean obt_xml_attr_bool (xmlNodePtr node, const gchar *name,
+ gboolean *value);
+
+G_END_DECLS
+
+#endif
diff --git a/obt/xqueue.c b/obt/xqueue.c
new file mode 100644
index 0000000..c04b226
--- /dev/null
+++ b/obt/xqueue.c
@@ -0,0 +1,411 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/display.c for the Openbox window manager
+ Copyright (c) 2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "obt/xqueue.h"
+#include "obt/display.h"
+
+#define MINSZ 16
+
+static XEvent *q = NULL;
+static gulong qsz = 0;
+static gulong qstart; /* the first event in the queue */
+static gulong qend; /* the last event in the queue */
+static gulong qnum = 0;
+
+static inline void shrink(void) {
+ if (qsz > MINSZ && qnum < qsz / 4) {
+ const gulong newsz = qsz/2;
+ gulong i;
+
+ if (qnum == 0) {
+ qstart = 0;
+ qend = -1;
+ }
+
+ /* all in the shinking part, move it to pos 0 */
+ else if (qstart >= newsz && qend >= newsz) {
+ for (i = 0; i < qnum; ++i)
+ q[i] = q[qstart+i];
+ qstart = 0;
+ qend = qnum - 1;
+ }
+
+ /* it wraps around to 0 right now, move the part between newsz and qsz
+ to be before newsz */
+ else if (qstart >= newsz) {
+ const gulong n = qsz - qstart;
+ for (i = 0; i < n; ++i)
+ q[newsz-n+i] = q[qstart+i];
+ qstart = newsz-n;
+ }
+
+ /* it needs to wrap around to 0, move the stuff after newsz to pos 0 */
+ else if (qend >= newsz) {
+ const gulong n = qend + 1 - newsz;
+ for (i = 0; i < n; ++i)
+ q[i] = q[newsz+i];
+ qend = n - 1;
+ }
+
+ q = g_renew(XEvent, q, newsz);
+ qsz = newsz;
+ }
+}
+
+static inline void grow(void) {
+ if (qnum == qsz) {
+ const gulong newsz = qsz*2;
+ gulong i;
+
+ q = g_renew(XEvent, q, newsz);
+
+ g_assert(qnum > 0);
+
+ if (qend < qstart) { /* it wraps around to 0 right now */
+ for (i = 0; i <= qend; ++i)
+ q[qsz+i] = q[i];
+ qend = qsz + qend;
+ }
+
+ qsz = newsz;
+ }
+}
+
+/* Grab all pending X events */
+static gboolean read_events(gboolean block)
+{
+ gint sth, n;
+
+ n = XEventsQueued(obt_display, QueuedAfterFlush) > 0;
+ sth = FALSE;
+
+ while ((block && !sth) || n > 0) {
+ XEvent e;
+
+ if (XNextEvent(obt_display, &e) != Success)
+ return FALSE;
+
+ grow(); /* make sure there is room */
+
+ ++qnum;
+ qend = (qend + 1) % qsz; /* move the end */
+ q[qend] = e; /* stick the event at the end */
+
+ --n;
+ sth = TRUE;
+ }
+
+ return sth; /* return if we read anything */
+}
+
+static void pop(const gulong p)
+{
+ /* remove the event */
+ --qnum;
+ if (qnum == 0) {
+ qstart = 0;
+ qend = -1;
+ }
+ else if (p == qstart)
+ qstart = (qstart + 1) % qsz;
+ else {
+ gulong pi;
+
+ /* is it cheaper to move the start or the end ? */
+ if ((p >= qstart && p < qstart + qnum/2) ||
+ (p < qstart && p < (qstart + qnum/2) % qsz))
+ {
+ /* move the start */
+ pi = p;
+ while (pi != qstart) {
+ const gulong pi_next = (pi == 0 ? qsz-1 : pi-1);
+
+ q[pi] = q[pi_next];
+ pi = pi_next;
+ }
+ qstart = (qstart + 1) % qsz;
+ }
+ else {
+ /* move the end */
+ pi = p;
+ while (pi != qend) {
+ const gulong pi_next = (pi + 1) % qsz;
+
+ q[pi] = q[pi_next];
+ pi = pi_next;
+ }
+ qend = (qend == 0 ? qsz-1 : qend-1);
+ }
+ }
+
+ shrink(); /* shrink the q if too little in it */
+}
+
+void xqueue_init(void)
+{
+ if (q != NULL) return;
+ qsz = MINSZ;
+ q = g_new(XEvent, qsz);
+ qstart = 0;
+ qend = -1;
+}
+
+void xqueue_destroy(void)
+{
+ if (q == NULL) return;
+ g_free(q);
+ q = NULL;
+ qsz = 0;
+}
+
+gboolean xqueue_match_window(XEvent *e, gpointer data)
+{
+ const Window w = *(Window*)data;
+ return e->xany.window == w;
+}
+
+gboolean xqueue_match_type(XEvent *e, gpointer data)
+{
+ return e->type == GPOINTER_TO_INT(data);
+}
+
+gboolean xqueue_match_window_type(XEvent *e, gpointer data)
+{
+ const ObtXQueueWindowType x = *(ObtXQueueWindowType*)data;
+ return e->xany.window == x.window && e->type == x.type;
+}
+
+gboolean xqueue_match_window_message(XEvent *e, gpointer data)
+{
+ const ObtXQueueWindowMessage x = *(ObtXQueueWindowMessage*)data;
+ return e->xany.window == x.window && e->type == ClientMessage &&
+ e->xclient.message_type == x.message;
+}
+
+gboolean xqueue_peek(XEvent *event_return)
+{
+ g_return_val_if_fail(q != NULL, FALSE);
+ g_return_val_if_fail(event_return != NULL, FALSE);
+
+ if (!qnum) read_events(TRUE);
+ if (!qnum) return FALSE;
+ *event_return = q[qstart]; /* get the head */
+ return TRUE;
+}
+
+gboolean xqueue_peek_local(XEvent *event_return)
+{
+ g_return_val_if_fail(q != NULL, FALSE);
+ g_return_val_if_fail(event_return != NULL, FALSE);
+
+ if (!qnum) read_events(FALSE);
+ if (!qnum) return FALSE;
+ *event_return = q[qstart]; /* get the head */
+ return TRUE;
+}
+
+gboolean xqueue_next(XEvent *event_return)
+{
+ g_return_val_if_fail(q != NULL, FALSE);
+ g_return_val_if_fail(event_return != NULL, FALSE);
+
+ if (!qnum) read_events(TRUE);
+ if (qnum) {
+ *event_return = q[qstart]; /* get the head */
+ pop(qstart);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean xqueue_next_local(XEvent *event_return)
+{
+ g_return_val_if_fail(q != NULL, FALSE);
+ g_return_val_if_fail(event_return != NULL, FALSE);
+
+ if (!qnum) read_events(FALSE);
+ if (qnum) {
+ *event_return = q[qstart]; /* get the head */
+ pop(qstart);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean xqueue_exists(xqueue_match_func match, gpointer data)
+{
+ gulong i, checked;
+
+ g_return_val_if_fail(q != NULL, FALSE);
+ g_return_val_if_fail(match != NULL, FALSE);
+
+ checked = 0;
+ while (TRUE) {
+ for (i = checked; i < qnum; ++i, ++checked) {
+ const gulong p = (qstart + i) % qsz;
+ if (match(&q[p], data))
+ return TRUE;
+ }
+ if (!read_events(TRUE)) break; /* error */
+ }
+ return FALSE;
+}
+
+gboolean xqueue_exists_local(xqueue_match_func match, gpointer data)
+{
+ gulong i, checked;
+
+ g_return_val_if_fail(q != NULL, FALSE);
+ g_return_val_if_fail(match != NULL, FALSE);
+
+ checked = 0;
+ while (TRUE) {
+ for (i = checked; i < qnum; ++i, ++checked) {
+ const gulong p = (qstart + i) % qsz;
+ if (match(&q[p], data))
+ return TRUE;
+ }
+ if (!read_events(FALSE)) break;
+ }
+ return FALSE;
+}
+
+gboolean xqueue_remove_local(XEvent *event_return,
+ xqueue_match_func match, gpointer data)
+{
+ gulong i, checked;
+
+ g_return_val_if_fail(q != NULL, FALSE);
+ g_return_val_if_fail(event_return != NULL, FALSE);
+ g_return_val_if_fail(match != NULL, FALSE);
+
+ checked = 0;
+ while (TRUE) {
+ for (i = checked; i < qnum; ++i, ++checked) {
+ const gulong p = (qstart + i) % qsz;
+ if (match(&q[p], data)) {
+ *event_return = q[p];
+ pop(p);
+ return TRUE;
+ }
+ }
+ if (!read_events(FALSE)) break;
+ }
+ return FALSE;
+}
+
+gboolean xqueue_pending_local(void)
+{
+ g_return_val_if_fail(q != NULL, FALSE);
+
+ if (!qnum) read_events(FALSE);
+ return qnum != 0;
+}
+
+typedef struct _ObtXQueueCB {
+ ObtXQueueFunc func;
+ gpointer data;
+} ObtXQueueCB;
+
+static ObtXQueueCB *callbacks = NULL;
+static guint n_callbacks = 0;
+
+static gboolean event_read(GSource *source, GSourceFunc callback,
+ gpointer data)
+{
+ XEvent ev;
+
+ while (xqueue_next_local(&ev)) {
+ guint i;
+ for (i = 0; i < n_callbacks; ++i)
+ callbacks[i].func(&ev, callbacks[i].data);
+ }
+
+ return TRUE; /* repeat */
+}
+
+static gboolean x_source_prepare(GSource *source, gint *timeout)
+{
+ *timeout = -1;
+ return XPending(obt_display);
+}
+
+static gboolean x_source_check(GSource *source)
+{
+ return XPending(obt_display);
+}
+
+struct x_source {
+ GSource source;
+
+ GPollFD pfd;
+};
+
+static GSourceFuncs x_source_funcs = {
+ x_source_prepare,
+ x_source_check,
+ event_read,
+ NULL
+};
+
+void xqueue_listen(void)
+{
+ GSource *source = g_source_new(&x_source_funcs, sizeof(struct x_source));
+ struct x_source *x_source = (struct x_source *)source;
+ GPollFD *pfd = &x_source->pfd;
+
+ *pfd = (GPollFD){ ConnectionNumber(obt_display), G_IO_IN, G_IO_IN };
+ g_source_add_poll(source, pfd);
+ g_source_attach(source, NULL);
+}
+
+void xqueue_add_callback(ObtXQueueFunc f, gpointer data)
+{
+ guint i;
+
+ g_return_if_fail(f != NULL);
+
+ for (i = 0; i < n_callbacks; ++i)
+ if (callbacks[i].func == f && callbacks[i].data == data)
+ return;
+
+ callbacks = g_renew(ObtXQueueCB, callbacks, n_callbacks + 1);
+ callbacks[n_callbacks].func = f;
+ callbacks[n_callbacks].data = data;
+ ++n_callbacks;
+}
+
+void xqueue_remove_callback(ObtXQueueFunc f, gpointer data)
+{
+ guint i;
+
+ g_return_if_fail(f != NULL);
+
+ for (i = 0; i < n_callbacks; ++i) {
+ if (callbacks[i].func == f && callbacks[i].data == data) {
+ /* remove it */
+ for (; i < n_callbacks - 1; ++i)
+ callbacks[i] = callbacks[i+1];
+ callbacks = g_renew(ObtXQueueCB, callbacks, n_callbacks - 1);
+ --n_callbacks;
+ break;
+ }
+ }
+}
diff --git a/obt/xqueue.h b/obt/xqueue.h
new file mode 100644
index 0000000..11cd2bc
--- /dev/null
+++ b/obt/xqueue.h
@@ -0,0 +1,101 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/xqueue.h for the Openbox window manager
+ Copyright (c) 2010 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __obt_xqueue_h
+#define __obt_xqueue_h
+
+#include <glib.h>
+#include <X11/Xlib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ObtXQueueWindowType {
+ Window window;
+ int type;
+} ObtXQueueWindowType;
+
+typedef struct _ObtXQueueWindowMessage {
+ Window window;
+ Atom message;
+} ObtXQueueWindowMessage;
+
+typedef gboolean (*xqueue_match_func)(XEvent *e, gpointer data);
+
+/*! Returns TRUE if the event matches the window pointed to by @data */
+gboolean xqueue_match_window(XEvent *e, gpointer data);
+
+/*! Returns TRUE if the event matches the type contained in the value of @data */
+gboolean xqueue_match_type(XEvent *e, gpointer data);
+
+/*! Returns TRUE if the event matches the type and window in the
+ ObtXQueueWindowType pointed to by @data */
+gboolean xqueue_match_window_type(XEvent *e, gpointer data);
+
+/*! Returns TRUE if a ClientMessage event matches the message and window in the
+ ObtXQueueWindowMessage pointed to by @data */
+gboolean xqueue_match_window_message(XEvent *e, gpointer data);
+
+/*! Returns TRUE and passes the next event in the queue and removes it from
+ the queue. On error, returns FALSE */
+gboolean xqueue_next(XEvent *event_return);
+
+/*! Returns TRUE and passes the next event in the local queue and removes it
+ from the queue. If no event is in the local queue, it returns FALSE. */
+gboolean xqueue_next_local(XEvent *event_return);
+
+/*! Returns TRUE if there is anything in the local event queue, and FALSE
+ otherwise. */
+gboolean xqueue_pending_local(void);
+
+/*! Returns TRUE and passes the next event in the queue, or FALSE if there
+ is an error */
+gboolean xqueue_peek(XEvent *event_return);
+
+/*! Returns TRUE and passes the next event in the queue, if there is one,
+ and returns FALSE otherwise. */
+gboolean xqueue_peek_local(XEvent *event_return);
+
+/*! Returns TRUE if xqueue_match_func returns TRUE for some event in the
+ current event queue or in the stream of events from the server,
+ and passes the matching event without removing it from the queue.
+ This blocks until an event is found or an error occurs. */
+gboolean xqueue_exists(xqueue_match_func match, gpointer data);
+
+/*! Returns TRUE if xqueue_match_func returns TRUE for some event in the
+ current event queue, and passes the matching event without removing it
+ from the queue. */
+gboolean xqueue_exists_local(xqueue_match_func match, gpointer data);
+
+/*! Returns TRUE if xqueue_match_func returns TRUE for some event in the
+ current event queue, and passes the matching event while removing it
+ from the queue. */
+gboolean xqueue_remove_local(XEvent *event_return,
+ xqueue_match_func match, gpointer data);
+
+typedef void (*ObtXQueueFunc)(const XEvent *ev, gpointer data);
+
+/*! Begin listening for X events in the default GMainContext, and feed them
+ to the registered callback functions, added with xqueue_add_callback(). */
+void xqueue_listen(void);
+
+void xqueue_add_callback(ObtXQueueFunc f, gpointer data);
+void xqueue_remove_callback(ObtXQueueFunc f, gpointer data);
+
+G_END_DECLS
+
+#endif
diff --git a/openbox/Makefile b/openbox/Makefile
new file mode 100644
index 0000000..b90edac
--- /dev/null
+++ b/openbox/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/openbox/actions.c b/openbox/actions.c
new file mode 100644
index 0000000..ee9d55f
--- /dev/null
+++ b/openbox/actions.c
@@ -0,0 +1,463 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ actions.c for the Openbox window manager
+ Copyright (c) 2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "actions.h"
+#include "gettext.h"
+#include "grab.h"
+#include "screen.h"
+#include "event.h"
+#include "config.h"
+#include "client.h"
+#include "focus.h"
+#include "openbox.h"
+#include "debug.h"
+
+#include "actions/all.h"
+
+static void actions_definition_ref(ObActionsDefinition *def);
+static void actions_definition_unref(ObActionsDefinition *def);
+static gboolean actions_interactive_begin_act(ObActionsAct *act, guint state);
+static void actions_interactive_end_act();
+static ObActionsAct* actions_build_act_from_string(const gchar *name);
+
+static ObActionsAct *interactive_act = NULL;
+static guint interactive_initial_state = 0;
+
+struct _ObActionsDefinition {
+ guint ref;
+
+ gchar *name;
+
+ gboolean canbeinteractive;
+ union {
+ ObActionsIDataSetupFunc i;
+ ObActionsDataSetupFunc n;
+ } setup;
+ ObActionsDataFreeFunc free;
+ ObActionsRunFunc run;
+ ObActionsShutdownFunc shutdown;
+};
+
+struct _ObActionsAct {
+ guint ref;
+
+ ObActionsDefinition *def;
+ ObActionsIPreFunc i_pre;
+ ObActionsIInputFunc i_input;
+ ObActionsICancelFunc i_cancel;
+ ObActionsIPostFunc i_post;
+ gpointer options;
+};
+
+static GSList *registered = NULL;
+
+void actions_startup(gboolean reconfig)
+{
+ if (reconfig) return;
+
+ action_all_startup();
+}
+
+void actions_shutdown(gboolean reconfig)
+{
+ actions_interactive_cancel_act();
+
+ if (reconfig) return;
+
+ /* free all the registered actions */
+ while (registered) {
+ ObActionsDefinition *d = registered->data;
+ if (d->shutdown) d->shutdown();
+ actions_definition_unref(d);
+ registered = g_slist_delete_link(registered, registered);
+ }
+}
+
+ObActionsDefinition* do_register(const gchar *name,
+ ObActionsDataFreeFunc free,
+ ObActionsRunFunc run)
+{
+ GSList *it;
+ ObActionsDefinition *def;
+
+ g_assert(run != NULL);
+
+ for (it = registered; it; it = g_slist_next(it)) {
+ def = it->data;
+ if (!g_ascii_strcasecmp(name, def->name)) /* already registered */
+ return NULL;
+ }
+
+ def = g_slice_new(ObActionsDefinition);
+ def->ref = 1;
+ def->name = g_strdup(name);
+ def->free = free;
+ def->run = run;
+ def->shutdown = NULL;
+
+ registered = g_slist_prepend(registered, def);
+ return def;
+}
+
+gboolean actions_register_i(const gchar *name,
+ ObActionsIDataSetupFunc setup,
+ ObActionsDataFreeFunc free,
+ ObActionsRunFunc run)
+{
+ ObActionsDefinition *def = do_register(name, free, run);
+ if (def) {
+ def->canbeinteractive = TRUE;
+ def->setup.i = setup;
+ }
+ return def != NULL;
+}
+
+gboolean actions_register(const gchar *name,
+ ObActionsDataSetupFunc setup,
+ ObActionsDataFreeFunc free,
+ ObActionsRunFunc run)
+{
+ ObActionsDefinition *def = do_register(name, free, run);
+ if (def) {
+ def->canbeinteractive = FALSE;
+ def->setup.n = setup;
+ }
+ return def != NULL;
+}
+
+gboolean actions_set_shutdown(const gchar *name,
+ ObActionsShutdownFunc shutdown)
+{
+ GSList *it;
+ ObActionsDefinition *def;
+
+ for (it = registered; it; it = g_slist_next(it)) {
+ def = it->data;
+ if (!g_ascii_strcasecmp(name, def->name)) {
+ def->shutdown = shutdown;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static void actions_definition_ref(ObActionsDefinition *def)
+{
+ ++def->ref;
+}
+
+static void actions_definition_unref(ObActionsDefinition *def)
+{
+ if (def && --def->ref == 0) {
+ g_free(def->name);
+ g_slice_free(ObActionsDefinition, def);
+ }
+}
+
+static ObActionsAct* actions_build_act_from_string(const gchar *name)
+{
+ GSList *it;
+ ObActionsDefinition *def = NULL;
+ ObActionsAct *act = NULL;
+
+ /* find the requested action */
+ for (it = registered; it; it = g_slist_next(it)) {
+ def = it->data;
+ if (!g_ascii_strcasecmp(name, def->name))
+ break;
+ def = NULL;
+ }
+
+ /* if we found the action */
+ if (def) {
+ act = g_slice_new(ObActionsAct);
+ act->ref = 1;
+ act->def = def;
+ actions_definition_ref(act->def);
+ act->i_pre = NULL;
+ act->i_input = NULL;
+ act->i_cancel = NULL;
+ act->i_post = NULL;
+ act->options = NULL;
+ } else
+ g_message(_("Invalid action \"%s\" requested. No such action exists."),
+ name);
+
+ return act;
+}
+
+ObActionsAct* actions_parse_string(const gchar *name)
+{
+ ObActionsAct *act = NULL;
+
+ if ((act = actions_build_act_from_string(name))) {
+ if (act->def->canbeinteractive) {
+ if (act->def->setup.i)
+ act->options = act->def->setup.i(NULL,
+ &act->i_pre,
+ &act->i_input,
+ &act->i_cancel,
+ &act->i_post);
+ }
+ else {
+ if (act->def->setup.n)
+ act->options = act->def->setup.n(NULL);
+ }
+ }
+
+
+ return act;
+}
+
+ObActionsAct* actions_parse(xmlNodePtr node)
+{
+ gchar *name;
+ ObActionsAct *act = NULL;
+
+ if (obt_xml_attr_string(node, "name", &name)) {
+ if ((act = actions_build_act_from_string(name))) {
+ /* there is more stuff to parse here */
+ if (act->def->canbeinteractive) {
+ if (act->def->setup.i)
+ act->options = act->def->setup.i(node->children,
+ &act->i_pre,
+ &act->i_input,
+ &act->i_cancel,
+ &act->i_post);
+ }
+ else {
+ if (act->def->setup.n)
+ act->options = act->def->setup.n(node->children);
+ }
+ }
+ g_free(name);
+ }
+
+ return act;
+}
+
+gboolean actions_act_is_interactive(ObActionsAct *act)
+{
+ return act->i_input != NULL;
+}
+
+void actions_act_ref(ObActionsAct *act)
+{
+ ++act->ref;
+}
+
+void actions_act_unref(ObActionsAct *act)
+{
+ if (act && --act->ref == 0) {
+ /* free the action specific options */
+ if (act->def->free)
+ act->def->free(act->options);
+ /* unref the definition */
+ actions_definition_unref(act->def);
+ g_slice_free(ObActionsAct, act);
+ }
+}
+
+static void actions_setup_data(ObActionsData *data,
+ ObUserAction uact,
+ guint state,
+ gint x,
+ gint y,
+ gint button,
+ ObFrameContext con,
+ struct _ObClient *client)
+{
+ data->uact = uact;
+ data->state = state;
+ data->x = x;
+ data->y = y;
+ data->button = button;
+ data->context = con;
+ data->client = client;
+}
+
+void actions_run_acts(GSList *acts,
+ ObUserAction uact,
+ guint state,
+ gint x,
+ gint y,
+ gint button,
+ ObFrameContext con,
+ struct _ObClient *client)
+{
+ GSList *it;
+ gboolean update_user_time;
+
+ /* Don't allow saving the initial state when running things from the
+ menu */
+ if (uact == OB_USER_ACTION_MENU_SELECTION)
+ state = 0;
+ /* If x and y are < 0 then use the current pointer position */
+ if (x < 0 && y < 0)
+ screen_pointer_pos(&x, &y);
+
+ update_user_time = FALSE;
+ for (it = acts; it; it = g_slist_next(it)) {
+ ObActionsData data;
+ ObActionsAct *act = it->data;
+ gboolean ok = TRUE;
+
+ actions_setup_data(&data, uact, state, x, y, button, con, client);
+
+ /* if they have the same run function, then we'll assume they are
+ cooperating and not cancel eachother out */
+ if (!interactive_act || interactive_act->def->run != act->def->run) {
+ if (actions_act_is_interactive(act)) {
+ /* cancel the old one */
+ if (interactive_act)
+ actions_interactive_cancel_act();
+ if (act->i_pre)
+ if (!act->i_pre(state, act->options))
+ act->i_input = NULL; /* remove the interactivity */
+ }
+ /* check again cuz it might have been cancelled */
+ if (actions_act_is_interactive(act))
+ ok = actions_interactive_begin_act(act, state);
+ }
+
+ /* fire the action's run function with this data */
+ if (ok) {
+ if (!act->def->run(&data, act->options)) {
+ if (actions_act_is_interactive(act))
+ actions_interactive_end_act();
+ if (client && client == focus_client)
+ update_user_time = TRUE;
+ } else {
+ /* make sure its interactive if it returned TRUE */
+ g_assert(act->i_input);
+
+ /* no actions are run after the interactive one */
+ break;
+ }
+ }
+ }
+ if (update_user_time)
+ event_update_user_time();
+}
+
+gboolean actions_interactive_act_running(void)
+{
+ return interactive_act != NULL;
+}
+
+void actions_interactive_cancel_act(void)
+{
+ if (interactive_act) {
+ if (interactive_act->i_cancel)
+ interactive_act->i_cancel(interactive_act->options);
+ actions_interactive_end_act();
+ }
+}
+
+static gboolean actions_interactive_begin_act(ObActionsAct *act, guint state)
+{
+ if (grab_keyboard()) {
+ interactive_act = act;
+ actions_act_ref(interactive_act);
+
+ interactive_initial_state = obt_keyboard_only_modmasks(state);
+
+ /* if using focus_delay, stop the timer now so that focus doesn't go
+ moving on us, which would kill the action */
+ event_halt_focus_delay();
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static void actions_interactive_end_act(void)
+{
+ if (interactive_act) {
+ ObActionsAct *ia = interactive_act;
+
+ /* set this to NULL first so the i_post() function can't cause this to
+ get called again (if it decides it wants to cancel any ongoing
+ interactive action). */
+ interactive_act = NULL;
+
+ ungrab_keyboard();
+
+ if (ia->i_post)
+ ia->i_post(ia->options);
+
+ actions_act_unref(ia);
+ }
+}
+
+gboolean actions_interactive_input_event(XEvent *e)
+{
+ gboolean used = FALSE;
+ if (interactive_act) {
+ if (!interactive_act->i_input(interactive_initial_state, e,
+ grab_input_context(),
+ interactive_act->options, &used))
+ {
+ used = TRUE; /* if it cancelled the action then it has to of
+ been used */
+ actions_interactive_end_act();
+ }
+ }
+ return used;
+}
+
+void actions_client_move(ObActionsData *data, gboolean start)
+{
+ static gulong ignore_start = 0;
+ if (start)
+ ignore_start = event_start_ignore_all_enters();
+ else if (config_focus_follow &&
+ data->context != OB_FRAME_CONTEXT_CLIENT)
+ {
+ if (data->uact == OB_USER_ACTION_MOUSE_PRESS) {
+ struct _ObClient *c;
+
+ /* usually this is sorta redundant, but with a press action
+ that moves windows our from under the cursor, the enter
+ event will come as a GrabNotify which is ignored, so this
+ makes a fake enter event
+
+ don't do this if there is a grab on the pointer. enter events
+ are ignored during a grab, so don't force fake ones when they
+ should be ignored
+ */
+ if (!grab_on_pointer()) {
+ if ((c = client_under_pointer()) && c != data->client) {
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Generating fake enter because we did a "
+ "mouse-event action");
+ event_enter_client(c);
+ }
+ else if (!c && c != data->client) {
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Generating fake leave because we did a "
+ "mouse-event action");
+ event_enter_client(data->client);
+ }
+ }
+ }
+ else if (!data->button && !config_focus_under_mouse)
+ event_end_ignore_all_enters(ignore_start);
+ }
+}
diff --git a/openbox/actions.h b/openbox/actions.h
new file mode 100644
index 0000000..e03bc57
--- /dev/null
+++ b/openbox/actions.h
@@ -0,0 +1,119 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ actions.h for the Openbox window manager
+ Copyright (c) 2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "misc.h"
+#include "frame.h"
+#include "obt/xml.h"
+#include "obt/keyboard.h"
+
+#include <glib.h>
+#include <X11/Xlib.h>
+
+typedef struct _ObActionsDefinition ObActionsDefinition;
+typedef struct _ObActionsAct ObActionsAct;
+typedef struct _ObActionsData ObActionsData;
+typedef struct _ObActionsAnyData ObActionsAnyData;
+typedef struct _ObActionsGlobalData ObActionsGlobalData;
+typedef struct _ObActionsClientData ObActionsClientData;
+typedef struct _ObActionsSelectorData ObActionsSelectorData;
+
+typedef void (*ObActionsDataFreeFunc)(gpointer options);
+typedef gboolean (*ObActionsRunFunc)(ObActionsData *data,
+ gpointer options);
+typedef gpointer (*ObActionsDataSetupFunc)(xmlNodePtr node);
+typedef void (*ObActionsShutdownFunc)(void);
+
+/* functions for interactive actions */
+/* return TRUE if the action is going to be interactive, or false to change
+ your mind and make it not */
+typedef gboolean (*ObActionsIPreFunc)(guint initial_state, gpointer options);
+typedef void (*ObActionsIPostFunc)(gpointer options);
+typedef gboolean (*ObActionsIInputFunc)(guint initial_state,
+ XEvent *e,
+ ObtIC *ic,
+ gpointer options,
+ gboolean *used);
+typedef void (*ObActionsICancelFunc)(gpointer options);
+typedef gpointer (*ObActionsIDataSetupFunc)(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+
+struct _ObActionsData {
+ ObUserAction uact;
+ guint state;
+ gint x;
+ gint y;
+ gint button;
+
+ struct _ObClient *client;
+ ObFrameContext context;
+};
+
+void actions_startup(gboolean reconfigure);
+void actions_shutdown(gboolean reconfigure);
+
+/*! Use this if the actions created from this name may be interactive */
+gboolean actions_register_i(const gchar *name,
+ ObActionsIDataSetupFunc setup,
+ ObActionsDataFreeFunc free,
+ ObActionsRunFunc run);
+
+gboolean actions_register(const gchar *name,
+ ObActionsDataSetupFunc setup,
+ ObActionsDataFreeFunc free,
+ ObActionsRunFunc run);
+
+gboolean actions_set_shutdown(const gchar *name,
+ ObActionsShutdownFunc shutdown);
+
+ObActionsAct* actions_parse(xmlNodePtr node);
+ObActionsAct* actions_parse_string(const gchar *name);
+
+gboolean actions_act_is_interactive(ObActionsAct *act);
+
+void actions_act_ref(ObActionsAct *act);
+void actions_act_unref(ObActionsAct *act);
+
+/*! When this is true, an XAllowEvents with ReplayPointer will be called
+ if an action is going to maybe try moving windows around on screen (or
+ map/unmap windows)
+*/
+void actions_set_need_pointer_replay_before_move(gboolean replay);
+/*! Returns if a ReplayPointer is still needed. If it was called while running
+ actions then this will be false */
+gboolean actions_get_need_pointer_replay_before_move(void);
+
+/*! Pass in a GSList of ObActionsAct's to be run. */
+void actions_run_acts(GSList *acts,
+ ObUserAction uact,
+ guint state,
+ gint x,
+ gint y,
+ gint button,
+ ObFrameContext con,
+ struct _ObClient *client);
+
+gboolean actions_interactive_act_running(void);
+void actions_interactive_cancel_act(void);
+
+gboolean actions_interactive_input_event(XEvent *e);
+
+/*! Function for actions to call when they are moving a client around */
+void actions_client_move(ObActionsData *data, gboolean start);
diff --git a/openbox/actions/Makefile b/openbox/actions/Makefile
new file mode 100644
index 0000000..b90edac
--- /dev/null
+++ b/openbox/actions/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/openbox/actions/addremovedesktop.c b/openbox/actions/addremovedesktop.c
new file mode 100644
index 0000000..ff6767e
--- /dev/null
+++ b/openbox/actions/addremovedesktop.c
@@ -0,0 +1,119 @@
+#include "openbox/actions.h"
+#include "openbox/screen.h"
+#include <glib.h>
+
+typedef struct {
+ gboolean current;
+ gboolean add;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static gpointer setup_add_func(xmlNodePtr node);
+static gpointer setup_remove_func(xmlNodePtr node);
+static void free_func(gpointer o);
+static gboolean run_func(ObActionsData *data, gpointer options);
+/* 3.4-compatibility */
+static gpointer setup_addcurrent_func(xmlNodePtr node);
+static gpointer setup_addlast_func(xmlNodePtr node);
+static gpointer setup_removecurrent_func(xmlNodePtr node);
+static gpointer setup_removelast_func(xmlNodePtr node);
+
+void action_addremovedesktop_startup(void)
+{
+ actions_register("AddDesktop", setup_add_func, free_func, run_func);
+ actions_register("RemoveDesktop", setup_remove_func, free_func, run_func);
+
+ /* 3.4-compatibility */
+ actions_register("AddDesktopLast", setup_addlast_func,
+ free_func, run_func);
+ actions_register("RemoveDesktopLast", setup_removelast_func,
+ free_func, run_func);
+ actions_register("AddDesktopCurrent", setup_addcurrent_func,
+ free_func, run_func);
+ actions_register("RemoveDesktopCurrent", setup_removecurrent_func,
+ free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+
+ if ((n = obt_xml_find_node(node, "where"))) {
+ gchar *s = obt_xml_node_string(n);
+ if (!g_ascii_strcasecmp(s, "last"))
+ o->current = FALSE;
+ else if (!g_ascii_strcasecmp(s, "current"))
+ o->current = TRUE;
+ g_free(s);
+ }
+
+ return o;
+}
+
+static gpointer setup_add_func(xmlNodePtr node)
+{
+ Options *o = setup_func(node);
+ o->add = TRUE;
+ return o;
+}
+
+static gpointer setup_remove_func(xmlNodePtr node)
+{
+ Options *o = setup_func(node);
+ o->add = FALSE;
+ return o;
+}
+
+static void free_func(gpointer o)
+{
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+
+ actions_client_move(data, TRUE);
+
+ if (o->add)
+ screen_add_desktop(o->current);
+ else
+ screen_remove_desktop(o->current);
+
+ actions_client_move(data, FALSE);
+
+ return FALSE;
+}
+
+/* 3.4-compatibility */
+static gpointer setup_addcurrent_func(xmlNodePtr node)
+{
+ Options *o = setup_add_func(node);
+ o->current = TRUE;
+ return o;
+}
+
+static gpointer setup_addlast_func(xmlNodePtr node)
+{
+ Options *o = setup_add_func(node);
+ o->current = FALSE;
+ return o;
+}
+
+static gpointer setup_removecurrent_func(xmlNodePtr node)
+{
+ Options *o = setup_remove_func(node);
+ o->current = TRUE;
+ return o;
+}
+
+static gpointer setup_removelast_func(xmlNodePtr node)
+{
+ Options *o = setup_remove_func(node);
+ o->current = FALSE;
+ return o;
+}
diff --git a/openbox/actions/all.c b/openbox/actions/all.c
new file mode 100644
index 0000000..332e79c
--- /dev/null
+++ b/openbox/actions/all.c
@@ -0,0 +1,44 @@
+#include "all.h"
+
+void action_all_startup(void)
+{
+ action_execute_startup();
+ action_debug_startup();
+ action_showmenu_startup();
+ action_showdesktop_startup();
+ action_reconfigure_startup();
+ action_exit_startup();
+ action_restart_startup();
+ action_cyclewindows_startup();
+ action_breakchroot_startup();
+ action_close_startup();
+ action_move_startup();
+ action_focus_startup();
+ action_raise_startup();
+ action_lower_startup();
+ action_raiselower_startup();
+ action_unfocus_startup();
+ action_iconify_startup();
+ action_fullscreen_startup();
+ action_maximize_startup();
+ action_moveresizeto_startup();
+ action_moverelative_startup();
+ action_shade_startup();
+ action_kill_startup();
+ action_omnipresent_startup();
+ action_directionalwindows_startup();
+ action_resize_startup();
+ action_decorations_startup();
+ action_desktop_startup();
+ action_dock_startup();
+ action_resizerelative_startup();
+ action_addremovedesktop_startup();
+ action_dockautohide_startup();
+ action_layer_startup();
+ action_movetoedge_startup();
+ action_growtoedge_startup();
+ action_if_startup();
+ action_focustobottom_startup();
+ /* 3.4-compatibility */
+ action_shadelowerraise_startup();
+}
diff --git a/openbox/actions/all.h b/openbox/actions/all.h
new file mode 100644
index 0000000..54d6319
--- /dev/null
+++ b/openbox/actions/all.h
@@ -0,0 +1,46 @@
+#ifndef __actions_all_h
+#define __actions_all_h
+
+void action_all_startup(void);
+
+void action_execute_startup(void);
+void action_debug_startup(void);
+void action_showmenu_startup(void);
+void action_showdesktop_startup(void);
+void action_reconfigure_startup(void);
+void action_exit_startup(void);
+void action_restart_startup(void);
+void action_cyclewindows_startup(void);
+void action_breakchroot_startup(void);
+void action_close_startup(void);
+void action_move_startup(void);
+void action_focus_startup(void);
+void action_raise_startup(void);
+void action_lower_startup(void);
+void action_raiselower_startup(void);
+void action_unfocus_startup(void);
+void action_iconify_startup(void);
+void action_fullscreen_startup(void);
+void action_maximize_startup(void);
+void action_moveresizeto_startup(void);
+void action_moverelative_startup(void);
+void action_shade_startup(void);
+void action_kill_startup(void);
+void action_omnipresent_startup(void);
+void action_directionalwindows_startup(void);
+void action_resize_startup(void);
+void action_decorations_startup(void);
+void action_desktop_startup(void);
+void action_dock_startup(void);
+void action_resizerelative_startup(void);
+void action_addremovedesktop_startup(void);
+void action_dockautohide_startup(void);
+void action_layer_startup(void);
+void action_movetoedge_startup(void);
+void action_growtoedge_startup(void);
+void action_if_startup(void);
+void action_focustobottom_startup(void);
+/* 3.4-compatibility */
+void action_shadelowerraise_startup(void);
+
+#endif
diff --git a/openbox/actions/breakchroot.c b/openbox/actions/breakchroot.c
new file mode 100644
index 0000000..8c00458
--- /dev/null
+++ b/openbox/actions/breakchroot.c
@@ -0,0 +1,20 @@
+#include "openbox/actions.h"
+#include "openbox/keyboard.h"
+
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_breakchroot_startup(void)
+{
+ actions_register("BreakChroot",
+ NULL, NULL,
+ run_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ /* break out of one chroot */
+ keyboard_reset_chains(1);
+
+ return FALSE;
+}
diff --git a/openbox/actions/close.c b/openbox/actions/close.c
new file mode 100644
index 0000000..d2bc96c
--- /dev/null
+++ b/openbox/actions/close.c
@@ -0,0 +1,19 @@
+#include "openbox/actions.h"
+#include "openbox/client.h"
+
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_close_startup(void)
+{
+ actions_register("Close",
+ NULL, NULL,
+ run_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ if (data->client) client_close(data->client);
+
+ return FALSE;
+}
diff --git a/openbox/actions/cyclewindows.c b/openbox/actions/cyclewindows.c
new file mode 100644
index 0000000..5f0db27
--- /dev/null
+++ b/openbox/actions/cyclewindows.c
@@ -0,0 +1,249 @@
+#include "openbox/actions.h"
+#include "openbox/stacking.h"
+#include "openbox/window.h"
+#include "openbox/event.h"
+#include "openbox/focus_cycle.h"
+#include "openbox/openbox.h"
+#include "gettext.h"
+#include "obt/keyboard.h"
+
+typedef struct {
+ gboolean linear;
+ gboolean dock_windows;
+ gboolean desktop_windows;
+ gboolean only_hilite_windows;
+ gboolean all_desktops;
+ gboolean forward;
+ gboolean bar;
+ gboolean raise;
+ ObFocusCyclePopupMode dialog_mode;
+ GSList *actions;
+
+
+ /* options for after we're done */
+ gboolean cancel; /* did the user cancel or not */
+ guint state; /* keyboard state when finished */
+} Options;
+
+static gpointer setup_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *in,
+ ObActionsICancelFunc *c,
+ ObActionsIPostFunc *post);
+static gpointer setup_forward_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *in,
+ ObActionsICancelFunc *c,
+ ObActionsIPostFunc *post);
+static gpointer setup_backward_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *in,
+ ObActionsICancelFunc *c,
+ ObActionsIPostFunc *post);
+static void free_func(gpointer options);
+static gboolean run_func(ObActionsData *data, gpointer options);
+static gboolean i_input_func(guint initial_state,
+ XEvent *e,
+ ObtIC *ic,
+ gpointer options,
+ gboolean *used);
+static void i_cancel_func(gpointer options);
+static void i_post_func(gpointer options);
+
+void action_cyclewindows_startup(void)
+{
+ actions_register_i("NextWindow", setup_forward_func, free_func, run_func);
+ actions_register_i("PreviousWindow", setup_backward_func, free_func,
+ run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+ o->bar = TRUE;
+ o->dialog_mode = OB_FOCUS_CYCLE_POPUP_MODE_LIST;
+
+ if ((n = obt_xml_find_node(node, "linear")))
+ o->linear = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "dialog"))) {
+ if (obt_xml_node_contains(n, "none"))
+ o->dialog_mode = OB_FOCUS_CYCLE_POPUP_MODE_NONE;
+ else if (obt_xml_node_contains(n, "no"))
+ o->dialog_mode = OB_FOCUS_CYCLE_POPUP_MODE_NONE;
+ else if (obt_xml_node_contains(n, "icons"))
+ o->dialog_mode = OB_FOCUS_CYCLE_POPUP_MODE_ICONS;
+ }
+ if ((n = obt_xml_find_node(node, "bar")))
+ o->bar = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "raise")))
+ o->raise = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "panels")))
+ o->dock_windows = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "hilite")))
+ o->only_hilite_windows = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "desktop")))
+ o->desktop_windows = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "allDesktops")))
+ o->all_desktops = obt_xml_node_bool(n);
+
+ if ((n = obt_xml_find_node(node, "finalactions"))) {
+ xmlNodePtr m;
+
+ m = obt_xml_find_node(n->children, "action");
+ while (m) {
+ ObActionsAct *action = actions_parse(m);
+ if (action) o->actions = g_slist_append(o->actions, action);
+ m = obt_xml_find_node(m->next, "action");
+ }
+ }
+ else {
+ o->actions = g_slist_prepend(o->actions,
+ actions_parse_string("Focus"));
+ o->actions = g_slist_prepend(o->actions,
+ actions_parse_string("Raise"));
+ o->actions = g_slist_prepend(o->actions,
+ actions_parse_string("Unshade"));
+ }
+
+ *input = i_input_func;
+ *cancel = i_cancel_func;
+ *post = i_post_func;
+ return o;
+}
+
+static gpointer setup_forward_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_func(node, pre, input, cancel, post);
+ o->forward = TRUE;
+ return o;
+}
+
+static gpointer setup_backward_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_func(node, pre, input, cancel, post);
+ o->forward = FALSE;
+ return o;
+}
+
+static void free_func(gpointer options)
+{
+ Options *o = options;
+
+ while (o->actions) {
+ actions_act_unref(o->actions->data);
+ o->actions = g_slist_delete_link(o->actions, o->actions);
+ }
+
+ g_slice_free(Options, o);
+}
+
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ 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);
+
+ stacking_restore();
+ if (o->raise && ft) stacking_temp_raise(CLIENT_AS_WINDOW(ft));
+
+ return TRUE;
+}
+
+static gboolean i_input_func(guint initial_state,
+ XEvent *e,
+ ObtIC *ic,
+ gpointer options,
+ gboolean *used)
+{
+ Options *o = options;
+ guint mods;
+
+ mods = obt_keyboard_only_modmasks(e->xkey.state);
+ if (e->type == KeyRelease) {
+ /* remove from the state the mask of the modifier key being
+ released, if it is a modifier key being released that is */
+ mods &= ~obt_keyboard_keyevent_to_modmask(e);
+ }
+
+ if (e->type == KeyPress) {
+ KeySym sym = obt_keyboard_keypress_to_keysym(e);
+
+ /* Escape cancels no matter what */
+ if (sym == XK_Escape) {
+ o->cancel = TRUE;
+ o->state = e->xkey.state;
+ return FALSE;
+ }
+
+ /* There were no modifiers and they pressed enter */
+ else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_state) {
+ o->cancel = FALSE;
+ o->state = e->xkey.state;
+ return FALSE;
+ }
+ }
+ /* They released the modifiers */
+ else if (e->type == KeyRelease && initial_state && !(mods & initial_state))
+ {
+ o->cancel = FALSE;
+ o->state = e->xkey.state;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void i_cancel_func(gpointer options)
+{
+ Options *o = options;
+ o->cancel = TRUE;
+ o->state = 0;
+}
+
+static void i_post_func(gpointer options)
+{
+ 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,
+ TRUE, o->cancel);
+
+ if (ft)
+ actions_run_acts(o->actions, OB_USER_ACTION_KEYBOARD_KEY,
+ o->state, -1, -1, 0, OB_FRAME_CONTEXT_NONE, ft);
+
+ stacking_restore();
+}
diff --git a/openbox/actions/debug.c b/openbox/actions/debug.c
new file mode 100644
index 0000000..99446bc
--- /dev/null
+++ b/openbox/actions/debug.c
@@ -0,0 +1,44 @@
+#include "openbox/actions.h"
+#include <glib.h>
+
+typedef struct {
+ gchar *str;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static void free_func(gpointer options);
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_debug_startup(void)
+{
+ actions_register("Debug", setup_func, free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+
+ if ((n = obt_xml_find_node(node, "string")))
+ o->str = obt_xml_node_string(n);
+ return o;
+}
+
+static void free_func(gpointer options)
+{
+ Options *o = options;
+ g_free(o->str);
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+
+ if (o->str) g_print("%s\n", o->str);
+
+ return FALSE;
+}
diff --git a/openbox/actions/decorations.c b/openbox/actions/decorations.c
new file mode 100644
index 0000000..f6fd2cb
--- /dev/null
+++ b/openbox/actions/decorations.c
@@ -0,0 +1,46 @@
+#include "openbox/actions.h"
+#include "openbox/client.h"
+
+static gboolean run_func_on(ObActionsData *data, gpointer options);
+static gboolean run_func_off(ObActionsData *data, gpointer options);
+static gboolean run_func_toggle(ObActionsData *data, gpointer options);
+
+void action_decorations_startup(void)
+{
+ actions_register("Decorate", NULL, NULL, run_func_on);
+ actions_register("Undecorate", NULL, NULL, run_func_off);
+ actions_register("ToggleDecorations", NULL, NULL, run_func_toggle);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func_on(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ client_set_undecorated(data->client, FALSE);
+ actions_client_move(data, FALSE);
+ }
+ return FALSE;
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func_off(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ client_set_undecorated(data->client, TRUE);
+ actions_client_move(data, FALSE);
+ }
+ return FALSE;
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func_toggle(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ client_set_undecorated(data->client, !data->client->undecorated);
+ actions_client_move(data, FALSE);
+ }
+ return FALSE;
+}
diff --git a/openbox/actions/desktop.c b/openbox/actions/desktop.c
new file mode 100644
index 0000000..a3a1f6b
--- /dev/null
+++ b/openbox/actions/desktop.c
@@ -0,0 +1,567 @@
+#include "openbox/actions.h"
+#include "openbox/screen.h"
+#include "openbox/client.h"
+#include "openbox/openbox.h"
+#include "obt/keyboard.h"
+
+typedef enum {
+ LAST,
+ CURRENT,
+ RELATIVE,
+ ABSOLUTE
+} SwitchType;
+
+typedef struct {
+ SwitchType type;
+ union {
+ struct {
+ guint desktop;
+ } abs;
+
+ struct {
+ gboolean linear;
+ gboolean wrap;
+ ObDirection dir;
+ } rel;
+ } u;
+ gboolean send;
+ gboolean follow;
+ gboolean interactive;
+} Options;
+
+static gpointer setup_go_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static gpointer setup_send_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static void free_func(gpointer o);
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+static gboolean i_pre_func(guint state, gpointer options);
+static gboolean i_input_func(guint initial_state,
+ XEvent *e,
+ ObtIC *ic,
+ gpointer options,
+ gboolean *used);
+static void i_post_func(gpointer options);
+
+/* 3.4-compatibility */
+static gpointer setup_go_last_func(xmlNodePtr node);
+static gpointer setup_send_last_func(xmlNodePtr node);
+static gpointer setup_go_abs_func(xmlNodePtr node);
+static gpointer setup_go_next_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static gpointer setup_send_next_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static gpointer setup_go_prev_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static gpointer setup_send_prev_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static gpointer setup_go_left_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static gpointer setup_send_left_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static gpointer setup_go_right_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static gpointer setup_send_right_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static gpointer setup_go_up_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static gpointer setup_send_up_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static gpointer setup_go_down_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static gpointer setup_send_down_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+
+void action_desktop_startup(void)
+{
+ actions_register_i("GoToDesktop", setup_go_func, free_func, run_func);
+ actions_register_i("SendToDesktop", setup_send_func, free_func, run_func);
+ /* 3.4-compatibility */
+ actions_register("DesktopLast", setup_go_last_func, free_func, run_func);
+ actions_register("SendToDesktopLast", setup_send_last_func,
+ free_func, run_func);
+ actions_register("Desktop", setup_go_abs_func, free_func, run_func);
+ actions_register_i("DesktopNext", setup_go_next_func, free_func, run_func);
+ actions_register_i("SendToDesktopNext", setup_send_next_func,
+ free_func, run_func);
+ actions_register_i("DesktopPrevious", setup_go_prev_func,
+ free_func, run_func);
+ actions_register_i("SendToDesktopPrevious", setup_send_prev_func,
+ free_func, run_func);
+ actions_register_i("DesktopLeft", setup_go_left_func, free_func, run_func);
+ actions_register_i("SendToDesktopLeft", setup_send_left_func,
+ free_func, run_func);
+ actions_register_i("DesktopRight", setup_go_right_func,
+ free_func, run_func);
+ actions_register_i("SendToDesktopRight", setup_send_right_func,
+ free_func, run_func);
+ actions_register_i("DesktopUp", setup_go_up_func, free_func, run_func);
+ actions_register_i("SendToDesktopUp", setup_send_up_func,
+ free_func, run_func);
+ actions_register_i("DesktopDown", setup_go_down_func, free_func, run_func);
+ actions_register_i("SendToDesktopDown", setup_send_down_func,
+ free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+ /* don't go anywhere if there are no options given */
+ o->type = ABSOLUTE;
+ o->u.abs.desktop = screen_desktop;
+ /* wrap by default - it's handy! */
+ o->u.rel.wrap = TRUE;
+
+ if ((n = obt_xml_find_node(node, "to"))) {
+ gchar *s = obt_xml_node_string(n);
+ if (!g_ascii_strcasecmp(s, "last"))
+ o->type = LAST;
+ else if (!g_ascii_strcasecmp(s, "current"))
+ o->type = CURRENT;
+ else if (!g_ascii_strcasecmp(s, "next")) {
+ o->type = RELATIVE;
+ o->u.rel.linear = TRUE;
+ o->u.rel.dir = OB_DIRECTION_EAST;
+ }
+ else if (!g_ascii_strcasecmp(s, "previous")) {
+ o->type = RELATIVE;
+ o->u.rel.linear = TRUE;
+ o->u.rel.dir = OB_DIRECTION_WEST;
+ }
+ else if (!g_ascii_strcasecmp(s, "north") ||
+ !g_ascii_strcasecmp(s, "up")) {
+ o->type = RELATIVE;
+ o->u.rel.dir = OB_DIRECTION_NORTH;
+ }
+ else if (!g_ascii_strcasecmp(s, "south") ||
+ !g_ascii_strcasecmp(s, "down")) {
+ o->type = RELATIVE;
+ o->u.rel.dir = OB_DIRECTION_SOUTH;
+ }
+ else if (!g_ascii_strcasecmp(s, "west") ||
+ !g_ascii_strcasecmp(s, "left")) {
+ o->type = RELATIVE;
+ o->u.rel.dir = OB_DIRECTION_WEST;
+ }
+ else if (!g_ascii_strcasecmp(s, "east") ||
+ !g_ascii_strcasecmp(s, "right")) {
+ o->type = RELATIVE;
+ o->u.rel.dir = OB_DIRECTION_EAST;
+ }
+ else {
+ o->type = ABSOLUTE;
+ o->u.abs.desktop = atoi(s) - 1;
+ }
+ g_free(s);
+ }
+
+ if ((n = obt_xml_find_node(node, "wrap")))
+ o->u.rel.wrap = obt_xml_node_bool(n);
+
+ return o;
+}
+
+
+static gpointer setup_go_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o;
+
+ o = setup_func(node, pre, input, cancel, post);
+ if (o->type == RELATIVE) {
+ o->interactive = TRUE;
+ *pre = i_pre_func;
+ *input = i_input_func;
+ *post = i_post_func;
+ }
+
+ return o;
+}
+
+static gpointer setup_send_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = setup_func(node, pre, input, cancel, post);
+ if ((n = obt_xml_find_node(node, "desktop"))) {
+ /* 3.4 compatibility */
+ o->u.abs.desktop = obt_xml_node_int(n) - 1;
+ o->type = ABSOLUTE;
+ }
+ o->send = TRUE;
+ o->follow = TRUE;
+
+ if ((n = obt_xml_find_node(node, "follow")))
+ o->follow = obt_xml_node_bool(n);
+
+ if (o->type == RELATIVE && o->follow) {
+ o->interactive = TRUE;
+ *pre = i_pre_func;
+ *input = i_input_func;
+ *post = i_post_func;
+ }
+
+ return o;
+}
+
+static void free_func(gpointer o)
+{
+ g_slice_free(Options, o);
+}
+
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+ guint d;
+
+ switch (o->type) {
+ case LAST:
+ d = screen_last_desktop;
+ break;
+ case CURRENT:
+ d = screen_desktop;
+ break;
+ case ABSOLUTE:
+ d = o->u.abs.desktop;
+ break;
+ case RELATIVE:
+ d = screen_find_desktop(screen_desktop,
+ o->u.rel.dir, o->u.rel.wrap, o->u.rel.linear);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (d < screen_num_desktops &&
+ (d != screen_desktop ||
+ (data->client && data->client->desktop != screen_desktop))) {
+ gboolean go = TRUE;
+
+ actions_client_move(data, TRUE);
+ if (o->send && data->client && client_normal(data->client)) {
+ client_set_desktop(data->client, d, o->follow, FALSE);
+ go = o->follow;
+ }
+
+ if (go) {
+ screen_set_desktop(d, TRUE);
+ if (data->client)
+ client_bring_helper_windows(data->client);
+ }
+
+ actions_client_move(data, FALSE);
+ }
+
+ return o->interactive;
+}
+
+static gboolean i_input_func(guint initial_state,
+ XEvent *e,
+ ObtIC *ic,
+ gpointer options,
+ gboolean *used)
+{
+ guint mods;
+
+ mods = obt_keyboard_only_modmasks(e->xkey.state);
+ if (e->type == KeyRelease) {
+ /* remove from the state the mask of the modifier key being
+ released, if it is a modifier key being released that is */
+ mods &= ~obt_keyboard_keyevent_to_modmask(e);
+ }
+
+ if (e->type == KeyPress) {
+ KeySym sym = obt_keyboard_keypress_to_keysym(e);
+
+ /* Escape cancels no matter what */
+ if (sym == XK_Escape)
+ return FALSE;
+
+ /* There were no modifiers and they pressed enter */
+ else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_state)
+ return FALSE;
+ }
+ /* They released the modifiers */
+ else if (e->type == KeyRelease && initial_state && !(mods & initial_state))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean i_pre_func(guint initial_state, gpointer options)
+{
+ if (!initial_state) {
+ Options *o = options;
+ o->interactive = FALSE;
+ return FALSE;
+ }
+ else {
+ screen_show_desktop_popup(screen_desktop, TRUE);
+ return TRUE;
+ }
+}
+
+static void i_post_func(gpointer options)
+{
+ screen_hide_desktop_popup();
+}
+
+/* 3.4-compatilibity */
+static gpointer setup_follow(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o = g_slice_new0(Options);
+ o->send = TRUE;
+ o->follow = TRUE;
+ if ((n = obt_xml_find_node(node, "follow")))
+ o->follow = obt_xml_node_bool(n);
+ return o;
+}
+
+static gpointer setup_go_last_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->type = LAST;
+ return o;
+}
+
+static gpointer setup_send_last_func(xmlNodePtr node)
+{
+ Options *o = setup_follow(node);
+ o->type = LAST;
+ return o;
+}
+
+static gpointer setup_go_abs_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o = g_slice_new0(Options);
+ o->type = ABSOLUTE;
+ if ((n = obt_xml_find_node(node, "desktop")))
+ o->u.abs.desktop = obt_xml_node_int(n) - 1;
+ else
+ o->u.abs.desktop = screen_desktop;
+ return o;
+}
+
+static void setup_rel(Options *o, xmlNodePtr node, gboolean lin,
+ ObDirection dir,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsIPostFunc *post)
+{
+ xmlNodePtr n;
+
+ o->type = RELATIVE;
+ o->u.rel.linear = lin;
+ o->u.rel.dir = dir;
+ o->u.rel.wrap = TRUE;
+
+ if ((n = obt_xml_find_node(node, "wrap")))
+ o->u.rel.wrap = obt_xml_node_bool(n);
+
+ if (input) {
+ o->interactive = TRUE;
+ *pre = i_pre_func;
+ *input = i_input_func;
+ *post = i_post_func;
+ }
+}
+
+static gpointer setup_go_next_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = g_slice_new0(Options);
+ setup_rel(o, node, TRUE, OB_DIRECTION_EAST, pre, input, post);
+ return o;
+}
+
+static gpointer setup_send_next_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_follow(node);
+ setup_rel(o, node, TRUE, OB_DIRECTION_EAST,
+ pre, (o->follow ? input : NULL), post);
+ return o;
+}
+
+static gpointer setup_go_prev_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = g_slice_new0(Options);
+ setup_rel(o, node, TRUE, OB_DIRECTION_WEST, pre, input, post);
+ return o;
+}
+
+static gpointer setup_send_prev_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_follow(node);
+ setup_rel(o, node, TRUE, OB_DIRECTION_WEST,
+ pre, (o->follow ? input : NULL), post);
+ return o;
+}
+
+static gpointer setup_go_left_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = g_slice_new0(Options);
+ setup_rel(o, node, FALSE, OB_DIRECTION_WEST, pre, input, post);
+ return o;
+}
+
+static gpointer setup_send_left_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_follow(node);
+ setup_rel(o, node, FALSE, OB_DIRECTION_WEST,
+ pre, (o->follow ? input : NULL), post);
+ return o;
+}
+
+static gpointer setup_go_right_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = g_slice_new0(Options);
+ setup_rel(o, node, FALSE, OB_DIRECTION_EAST, pre, input, post);
+ return o;
+}
+
+static gpointer setup_send_right_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_follow(node);
+ setup_rel(o, node, FALSE, OB_DIRECTION_EAST,
+ pre, (o->follow ? input : NULL), post);
+ return o;
+}
+
+static gpointer setup_go_up_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = g_slice_new0(Options);
+ setup_rel(o, node, FALSE, OB_DIRECTION_NORTH, pre, input, post);
+ return o;
+}
+
+static gpointer setup_send_up_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_follow(node);
+ setup_rel(o, node, FALSE, OB_DIRECTION_NORTH,
+ pre, (o->follow ? input : NULL), post);
+ return o;
+}
+
+static gpointer setup_go_down_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = g_slice_new0(Options);
+ setup_rel(o, node, FALSE, OB_DIRECTION_SOUTH, pre, input, post);
+ return o;
+}
+
+static gpointer setup_send_down_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_follow(node);
+ setup_rel(o, node, FALSE, OB_DIRECTION_SOUTH,
+ pre, (o->follow ? input : NULL), post);
+ return o;
+}
diff --git a/openbox/actions/directionalwindows.c b/openbox/actions/directionalwindows.c
new file mode 100644
index 0000000..602e7ed
--- /dev/null
+++ b/openbox/actions/directionalwindows.c
@@ -0,0 +1,468 @@
+#include "openbox/actions.h"
+#include "openbox/event.h"
+#include "openbox/stacking.h"
+#include "openbox/window.h"
+#include "openbox/focus_cycle.h"
+#include "openbox/openbox.h"
+#include "openbox/misc.h"
+#include "gettext.h"
+#include "obt/keyboard.h"
+
+typedef struct {
+ gboolean interactive;
+ gboolean dialog;
+ gboolean dock_windows;
+ gboolean desktop_windows;
+ ObDirection direction;
+ gboolean bar;
+ gboolean raise;
+ GSList *actions;
+} Options;
+
+static gboolean cycling = FALSE;
+
+static gpointer setup_func(xmlNodePtr node);
+static gpointer setup_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post);
+static gpointer setup_target_func(xmlNodePtr node);
+static void free_func(gpointer options);
+static gboolean run_func(ObActionsData *data, gpointer options);
+static gboolean i_input_func(guint initial_state,
+ XEvent *e,
+ ObtIC *ic,
+ gpointer options,
+ gboolean *used);
+static void i_cancel_func(gpointer options);
+
+static void end_cycle(gboolean cancel, guint state, Options *o);
+
+/* 3.4-compatibility */
+static gpointer setup_north_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *in,
+ ObActionsICancelFunc *c,
+ ObActionsIPostFunc *post);
+static gpointer setup_south_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *in,
+ ObActionsICancelFunc *c,
+ ObActionsIPostFunc *post);
+static gpointer setup_east_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *in,
+ ObActionsICancelFunc *c,
+ ObActionsIPostFunc *post);
+static gpointer setup_west_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *in,
+ ObActionsICancelFunc *c,
+ ObActionsIPostFunc *post);
+static gpointer setup_northwest_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *in,
+ ObActionsICancelFunc *c,
+ ObActionsIPostFunc *post);
+static gpointer setup_northeast_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *in,
+ ObActionsICancelFunc *c,
+ ObActionsIPostFunc *post);
+static gpointer setup_southwest_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *in,
+ ObActionsICancelFunc *c,
+ ObActionsIPostFunc *post);
+static gpointer setup_southeast_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *in,
+ ObActionsICancelFunc *c,
+ ObActionsIPostFunc *post);
+static gpointer setup_north_target_func(xmlNodePtr node);
+static gpointer setup_south_target_func(xmlNodePtr node);
+static gpointer setup_east_target_func(xmlNodePtr node);
+static gpointer setup_west_target_func(xmlNodePtr node);
+static gpointer setup_northwest_target_func(xmlNodePtr node);
+static gpointer setup_northeast_target_func(xmlNodePtr node);
+static gpointer setup_southwest_target_func(xmlNodePtr node);
+static gpointer setup_southeast_target_func(xmlNodePtr node);
+
+void action_directionalwindows_startup(void)
+{
+ actions_register_i("DirectionalCycleWindows", setup_cycle_func, free_func,
+ run_func);
+ actions_register("DirectionalTargetWindow", setup_target_func, free_func,
+ run_func);
+ /* 3.4-compatibility */
+ actions_register_i("DirectionalFocusNorth", setup_north_cycle_func,
+ free_func, run_func);
+ actions_register_i("DirectionalFocusSouth", setup_south_cycle_func,
+ free_func, run_func);
+ actions_register_i("DirectionalFocusWest", setup_west_cycle_func,
+ free_func, run_func);
+ actions_register_i("DirectionalFocusEast", setup_east_cycle_func,
+ free_func, run_func);
+ actions_register_i("DirectionalFocusNorthWest", setup_northwest_cycle_func,
+ free_func, run_func);
+ actions_register_i("DirectionalFocusNorthEast", setup_northeast_cycle_func,
+ free_func, run_func);
+ actions_register_i("DirectionalFocusSouthWest", setup_southwest_cycle_func,
+ free_func, run_func);
+ actions_register_i("DirectionalFocusSouthEast", setup_southeast_cycle_func,
+ free_func, run_func);
+ actions_register("DirectionalTargetNorth", setup_north_target_func,
+ free_func, run_func);
+ actions_register("DirectionalTargetSouth", setup_south_target_func,
+ free_func, run_func);
+ actions_register("DirectionalTargetWest", setup_west_target_func,
+ free_func, run_func);
+ actions_register("DirectionalTargetEast", setup_east_target_func,
+ free_func, run_func);
+ actions_register("DirectionalTargetNorthWest", setup_northwest_target_func,
+ free_func, run_func);
+ actions_register("DirectionalTargetNorthEast", setup_northeast_target_func,
+ free_func, run_func);
+ actions_register("DirectionalTargetSouthWest", setup_southwest_target_func,
+ free_func, run_func);
+ actions_register("DirectionalTargetSouthEast", setup_southeast_target_func,
+ free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+ o->dialog = TRUE;
+ o->bar = TRUE;
+
+ if ((n = obt_xml_find_node(node, "dialog")))
+ o->dialog = 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")))
+ o->raise = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "panels")))
+ o->dock_windows = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "desktop")))
+ o->desktop_windows = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "direction"))) {
+ gchar *s = obt_xml_node_string(n);
+ if (!g_ascii_strcasecmp(s, "north") ||
+ !g_ascii_strcasecmp(s, "up"))
+ o->direction = OB_DIRECTION_NORTH;
+ else if (!g_ascii_strcasecmp(s, "northwest"))
+ o->direction = OB_DIRECTION_NORTHWEST;
+ else if (!g_ascii_strcasecmp(s, "northeast"))
+ o->direction = OB_DIRECTION_NORTHEAST;
+ else if (!g_ascii_strcasecmp(s, "west") ||
+ !g_ascii_strcasecmp(s, "left"))
+ o->direction = OB_DIRECTION_WEST;
+ else if (!g_ascii_strcasecmp(s, "east") ||
+ !g_ascii_strcasecmp(s, "right"))
+ o->direction = OB_DIRECTION_EAST;
+ else if (!g_ascii_strcasecmp(s, "south") ||
+ !g_ascii_strcasecmp(s, "down"))
+ o->direction = OB_DIRECTION_SOUTH;
+ else if (!g_ascii_strcasecmp(s, "southwest"))
+ o->direction = OB_DIRECTION_SOUTHWEST;
+ else if (!g_ascii_strcasecmp(s, "southeast"))
+ o->direction = OB_DIRECTION_SOUTHEAST;
+ g_free(s);
+ }
+
+ if ((n = obt_xml_find_node(node, "finalactions"))) {
+ xmlNodePtr m;
+
+ m = obt_xml_find_node(n->children, "action");
+ while (m) {
+ ObActionsAct *action = actions_parse(m);
+ if (action) o->actions = g_slist_append(o->actions, action);
+ m = obt_xml_find_node(m->next, "action");
+ }
+ }
+ else {
+ o->actions = g_slist_prepend(o->actions,
+ actions_parse_string("Focus"));
+ o->actions = g_slist_prepend(o->actions,
+ actions_parse_string("Raise"));
+ o->actions = g_slist_prepend(o->actions,
+ actions_parse_string("Unshade"));
+ }
+
+ return o;
+}
+
+static gpointer setup_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_func(node);
+ o->interactive = TRUE;
+ *input = i_input_func;
+ *cancel = i_cancel_func;
+ return o;
+}
+
+static gpointer setup_target_func(xmlNodePtr node)
+{
+ Options *o = setup_func(node);
+ o->interactive = FALSE;
+ return o;
+}
+
+static void free_func(gpointer options)
+{
+ Options *o = options;
+
+ while (o->actions) {
+ actions_act_unref(o->actions->data);
+ o->actions = g_slist_delete_link(o->actions, o->actions);
+ }
+
+ g_slice_free(Options, o);
+}
+
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+
+ if (!o->interactive)
+ end_cycle(FALSE, data->state, o);
+ else {
+ struct _ObClient *ft;
+
+ ft = focus_directional_cycle(o->direction,
+ o->dock_windows,
+ o->desktop_windows,
+ TRUE,
+ o->bar,
+ o->dialog,
+ FALSE, FALSE);
+ cycling = TRUE;
+
+ stacking_restore();
+ if (o->raise && ft) stacking_temp_raise(CLIENT_AS_WINDOW(ft));
+ }
+
+ return o->interactive;
+}
+
+static gboolean i_input_func(guint initial_state,
+ XEvent *e,
+ ObtIC *ic,
+ gpointer options,
+ gboolean *used)
+{
+ guint mods;
+
+ mods = obt_keyboard_only_modmasks(e->xkey.state);
+ if (e->type == KeyRelease) {
+ /* remove from the state the mask of the modifier key being
+ released, if it is a modifier key being released that is */
+ mods &= ~obt_keyboard_keyevent_to_modmask(e);
+ }
+
+ if (e->type == KeyPress) {
+ KeySym sym = obt_keyboard_keypress_to_keysym(e);
+
+ /* Escape cancels no matter what */
+ if (sym == XK_Escape) {
+ end_cycle(TRUE, e->xkey.state, options);
+ return FALSE;
+ }
+
+ /* There were no modifiers and they pressed enter */
+ else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_state) {
+ end_cycle(FALSE, e->xkey.state, options);
+ return FALSE;
+ }
+ }
+ /* They released the modifiers */
+ else if (e->type == KeyRelease && initial_state && !(mods & initial_state))
+ {
+ end_cycle(FALSE, e->xkey.state, options);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void i_cancel_func(gpointer options)
+{
+ /* we get cancelled when we move focus, but we're not cycling anymore, so
+ just ignore that */
+ if (cycling)
+ end_cycle(TRUE, 0, options);
+}
+
+static void end_cycle(gboolean cancel, guint state, Options *o)
+{
+ struct _ObClient *ft;
+
+ ft = focus_directional_cycle(o->direction,
+ o->dock_windows,
+ o->desktop_windows,
+ o->interactive,
+ o->bar,
+ o->dialog,
+ TRUE, cancel);
+ cycling = FALSE;
+
+ if (ft)
+ actions_run_acts(o->actions, OB_USER_ACTION_KEYBOARD_KEY,
+ state, -1, -1, 0, OB_FRAME_CONTEXT_NONE, ft);
+
+ stacking_restore();
+}
+
+/* 3.4-compatibility */
+static gpointer setup_north_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_cycle_func(node, pre, input, cancel, post);
+ o->direction = OB_DIRECTION_NORTH;
+ return o;
+}
+
+static gpointer setup_south_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_cycle_func(node, pre, input, cancel, post);
+ o->direction = OB_DIRECTION_SOUTH;
+ return o;
+}
+
+static gpointer setup_east_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_cycle_func(node, pre, input, cancel, post);
+ o->direction = OB_DIRECTION_EAST;
+ return o;
+}
+
+static gpointer setup_west_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_cycle_func(node, pre, input, cancel, post);
+ o->direction = OB_DIRECTION_WEST;
+ return o;
+}
+
+static gpointer setup_northwest_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_cycle_func(node, pre, input, cancel, post);
+ o->direction = OB_DIRECTION_NORTHWEST;
+ return o;
+}
+
+static gpointer setup_northeast_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_cycle_func(node, pre, input, cancel, post);
+ o->direction = OB_DIRECTION_EAST;
+ return o;
+}
+
+static gpointer setup_southwest_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_cycle_func(node, pre, input, cancel, post);
+ o->direction = OB_DIRECTION_SOUTHWEST;
+ return o;
+}
+
+static gpointer setup_southeast_cycle_func(xmlNodePtr node,
+ ObActionsIPreFunc *pre,
+ ObActionsIInputFunc *input,
+ ObActionsICancelFunc *cancel,
+ ObActionsIPostFunc *post)
+{
+ Options *o = setup_cycle_func(node, pre, input, cancel, post);
+ o->direction = OB_DIRECTION_SOUTHEAST;
+ return o;
+}
+
+static gpointer setup_north_target_func(xmlNodePtr node)
+{
+ Options *o = setup_target_func(node);
+ o->direction = OB_DIRECTION_NORTH;
+ return o;
+}
+
+static gpointer setup_south_target_func(xmlNodePtr node)
+{
+ Options *o = setup_target_func(node);
+ o->direction = OB_DIRECTION_SOUTH;
+ return o;
+}
+
+static gpointer setup_east_target_func(xmlNodePtr node)
+{
+ Options *o = setup_target_func(node);
+ o->direction = OB_DIRECTION_EAST;
+ return o;
+}
+
+static gpointer setup_west_target_func(xmlNodePtr node)
+{
+ Options *o = setup_target_func(node);
+ o->direction = OB_DIRECTION_WEST;
+ return o;
+}
+
+static gpointer setup_northwest_target_func(xmlNodePtr node)
+{
+ Options *o = setup_target_func(node);
+ o->direction = OB_DIRECTION_NORTHWEST;
+ return o;
+}
+
+static gpointer setup_northeast_target_func(xmlNodePtr node)
+{
+ Options *o = setup_target_func(node);
+ o->direction = OB_DIRECTION_NORTHEAST;
+ return o;
+}
+
+static gpointer setup_southwest_target_func(xmlNodePtr node)
+{
+ Options *o = setup_target_func(node);
+ o->direction = OB_DIRECTION_SOUTHWEST;
+ return o;
+}
+
+static gpointer setup_southeast_target_func(xmlNodePtr node)
+{
+ Options *o = setup_target_func(node);
+ o->direction = OB_DIRECTION_SOUTHEAST;
+ return o;
+}
+
diff --git a/openbox/actions/dock.c b/openbox/actions/dock.c
new file mode 100644
index 0000000..a1f6837
--- /dev/null
+++ b/openbox/actions/dock.c
@@ -0,0 +1,38 @@
+#include "openbox/actions.h"
+#include "openbox/stacking.h"
+#include "openbox/window.h"
+#include "openbox/dock.h"
+
+static gboolean raise_func(ObActionsData *data, gpointer options);
+static gboolean lower_func(ObActionsData *data, gpointer options);
+
+void action_dock_startup(void)
+{
+ actions_register("RaiseDock",
+ NULL, NULL,
+ raise_func);
+ actions_register("LowerDock",
+ NULL, NULL,
+ lower_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean raise_func(ObActionsData *data, gpointer options)
+{
+ actions_client_move(data, TRUE);
+ dock_raise_dock();
+ actions_client_move(data, FALSE);
+
+ return FALSE;
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean lower_func(ObActionsData *data, gpointer options)
+{
+ actions_client_move(data, TRUE);
+ dock_lower_dock();
+ actions_client_move(data, FALSE);
+
+ return FALSE;
+}
+
diff --git a/openbox/actions/dockautohide.c b/openbox/actions/dockautohide.c
new file mode 100644
index 0000000..4a750b2
--- /dev/null
+++ b/openbox/actions/dockautohide.c
@@ -0,0 +1,21 @@
+#include "openbox/actions.h"
+#include "openbox/dock.h"
+#include "openbox/config.h"
+
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_dockautohide_startup(void)
+{
+ actions_register("ToggleDockAutoHide",
+ NULL, NULL,
+ run_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ config_dock_hide = !config_dock_hide;
+ dock_configure();
+
+ return FALSE;
+}
diff --git a/openbox/actions/execute.c b/openbox/actions/execute.c
new file mode 100644
index 0000000..380ffa0
--- /dev/null
+++ b/openbox/actions/execute.c
@@ -0,0 +1,273 @@
+#include "openbox/actions.h"
+#include "openbox/event.h"
+#include "openbox/startupnotify.h"
+#include "openbox/client.h"
+#include "openbox/prompt.h"
+#include "openbox/screen.h"
+#include "obt/paths.h"
+#include "gettext.h"
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+typedef struct {
+ gchar *cmd;
+ gboolean sn;
+ gchar *sn_name;
+ gchar *sn_icon;
+ gchar *sn_wmclass;
+ gchar *prompt;
+ ObActionsData *data;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static void free_func(gpointer options);
+static gboolean run_func(ObActionsData *data, gpointer options);
+static void shutdown_func(void);
+static void client_dest(ObClient *client, gpointer data);
+
+static GSList *prompt_opts = NULL;
+
+void action_execute_startup(void)
+{
+ actions_register("Execute", setup_func, free_func, run_func);
+ actions_set_shutdown("Execute", shutdown_func);
+
+ client_add_destroy_notify(client_dest, NULL);
+}
+
+static void client_dest(ObClient *client, gpointer data)
+{
+ GSList *it;
+
+ for (it = prompt_opts; it; it = g_slist_next(it)) {
+ Options *o = it->data;
+ if (o->data->client == client)
+ o->data->client = NULL;
+ }
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+
+ if ((n = obt_xml_find_node(node, "command")) ||
+ (n = obt_xml_find_node(node, "execute")))
+ {
+ gchar *s = obt_xml_node_string(n);
+ o->cmd = obt_paths_expand_tilde(s);
+ g_free(s);
+ }
+
+ if ((n = obt_xml_find_node(node, "prompt")))
+ o->prompt = obt_xml_node_string(n);
+
+ if ((n = obt_xml_find_node(node, "startupnotify"))) {
+ xmlNodePtr m;
+ if ((m = obt_xml_find_node(n->children, "enabled")))
+ o->sn = obt_xml_node_bool(m);
+ if ((m = obt_xml_find_node(n->children, "name")))
+ o->sn_name = obt_xml_node_string(m);
+ if ((m = obt_xml_find_node(n->children, "icon")))
+ o->sn_icon = obt_xml_node_string(m);
+ if ((m = obt_xml_find_node(n->children, "wmclass")))
+ o->sn_wmclass = obt_xml_node_string(m);
+ }
+ return o;
+}
+
+static void shutdown_func(void)
+{
+ client_remove_destroy_notify(client_dest);
+}
+
+static void free_func(gpointer options)
+{
+ Options *o = options;
+
+ if (o) {
+ prompt_opts = g_slist_remove(prompt_opts, o);
+
+ g_free(o->cmd);
+ g_free(o->sn_name);
+ g_free(o->sn_icon);
+ g_free(o->sn_wmclass);
+ g_free(o->prompt);
+ if (o->data) g_slice_free(ObActionsData, o->data);
+ g_slice_free(Options, o);
+ }
+}
+
+static Options* dup_options(Options *in, ObActionsData *data)
+{
+ Options *o = g_slice_new(Options);
+ o->cmd = g_strdup(in->cmd);
+ o->sn = in->sn;
+ o->sn_name = g_strdup(in->sn_name);
+ o->sn_icon = g_strdup(in->sn_icon);
+ o->sn_wmclass = g_strdup(in->sn_wmclass);
+ o->prompt = NULL;
+ o->data = g_slice_new(ObActionsData);
+ memcpy(o->data, data, sizeof(ObActionsData));
+ return o;
+}
+
+static gboolean prompt_cb(ObPrompt *p, gint result, gpointer options)
+{
+ Options *o = options;
+ if (result)
+ run_func(o->data, o);
+ return TRUE; /* call the cleanup func */
+}
+
+static void prompt_cleanup(ObPrompt *p, gpointer options)
+{
+ prompt_unref(p);
+ free_func(options);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ GError *e;
+ gchar **argv = NULL;
+ gchar *cmd;
+ Options *o = options;
+
+ if (!o->cmd) return FALSE;
+
+ if (o->prompt) {
+ ObPrompt *p;
+ Options *ocp;
+ ObPromptAnswer answers[] = {
+ { _("No"), 0 },
+ { _("Yes"), 1 }
+ };
+
+ ocp = dup_options(options, data);
+ p = prompt_new(o->prompt, _("Execute"), answers, 2, 0, 0,
+ prompt_cb, prompt_cleanup, ocp);
+ prompt_show(p, NULL, FALSE);
+
+ return FALSE;
+ }
+
+ cmd = g_filename_from_utf8(o->cmd, -1, NULL, NULL, NULL);
+ if (!cmd) {
+ g_message(_("Failed to convert the path \"%s\" from utf8"), o->cmd);
+ 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;
+ }
+ }
+
+ /* If there is a keyboard grab going on then we need to cancel
+ it so the application can grab things */
+ if (data->uact != OB_USER_ACTION_MENU_SELECTION)
+ event_cancel_all_key_grabs();
+
+ e = NULL;
+ if (!g_shell_parse_argv(cmd, NULL, &argv, &e)) {
+ g_message("%s", e->message);
+ g_error_free(e);
+ }
+ else {
+ gchar *program = NULL;
+ gboolean ok;
+
+ if (o->sn) {
+ program = g_path_get_basename(argv[0]);
+ /* sets up the environment */
+ sn_setup_spawn_environment(program, o->sn_name, o->sn_icon,
+ o->sn_wmclass,
+ /* launch it on the current desktop */
+ screen_desktop);
+ }
+
+ e = NULL;
+ ok = g_spawn_async(NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH |
+ G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL, NULL, NULL, &e);
+ if (!ok) {
+ g_message("%s", e->message);
+ g_error_free(e);
+ }
+
+ if (o->sn) {
+ if (!ok) sn_spawn_cancel();
+ g_unsetenv("DESKTOP_STARTUP_ID");
+ }
+
+ g_free(program);
+ g_strfreev(argv);
+ }
+
+ g_free(cmd);
+
+ return FALSE;
+}
diff --git a/openbox/actions/exit.c b/openbox/actions/exit.c
new file mode 100644
index 0000000..2d9fc63
--- /dev/null
+++ b/openbox/actions/exit.c
@@ -0,0 +1,88 @@
+#include "openbox/actions.h"
+#include "openbox/openbox.h"
+#include "openbox/prompt.h"
+#include "openbox/session.h"
+#include "gettext.h"
+
+typedef struct {
+ gboolean prompt;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static void free_func(gpointer o);
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_exit_startup(void)
+{
+ actions_register("Exit", setup_func, free_func, run_func);
+ actions_register("SessionLogout", setup_func, free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+ o->prompt = TRUE;
+
+ if ((n = obt_xml_find_node(node, "prompt")))
+ o->prompt = obt_xml_node_bool(n);
+
+ return o;
+}
+
+static void free_func(gpointer o)
+{
+ g_slice_free(Options, o);
+}
+
+static void do_exit(void)
+{
+ if (session_connected())
+ session_request_logout(FALSE);
+ else
+ ob_exit(0);
+}
+
+static gboolean prompt_cb(ObPrompt *p, gint result, gpointer data)
+{
+ if (result)
+ do_exit();
+ return TRUE; /* call the cleanup func */
+}
+
+static void prompt_cleanup(ObPrompt *p, gpointer data)
+{
+ prompt_unref(p);
+}
+
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+
+ if (o->prompt) {
+ ObPrompt *p;
+ ObPromptAnswer answers[] = {
+ { _("Cancel"), 0 },
+ { _("Exit"), 1 }
+ };
+
+ if (session_connected())
+ p = prompt_new(_("Are you sure you want to log out?"),
+ _("Log Out"),
+ answers, 2, 0, 0, prompt_cb, prompt_cleanup, NULL);
+ else
+ p = prompt_new(_("Are you sure you want to exit Openbox?"),
+ _("Exit Openbox"),
+ answers, 2, 0, 0, prompt_cb, prompt_cleanup, NULL);
+
+ prompt_show(p, NULL, FALSE);
+ }
+ else
+ do_exit();
+
+ return FALSE;
+}
diff --git a/openbox/actions/focus.c b/openbox/actions/focus.c
new file mode 100644
index 0000000..6c8957c
--- /dev/null
+++ b/openbox/actions/focus.c
@@ -0,0 +1,74 @@
+#include "openbox/actions.h"
+#include "openbox/event.h"
+#include "openbox/client.h"
+#include "openbox/focus.h"
+#include "openbox/screen.h"
+
+typedef struct {
+ gboolean here;
+ gboolean stop_int;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static void free_func(gpointer o);
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_focus_startup(void)
+{
+ actions_register("Focus", setup_func, free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+ o->stop_int = TRUE;
+
+ if ((n = obt_xml_find_node(node, "here")))
+ o->here = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "stopInteractive")))
+ o->stop_int = obt_xml_node_bool(n);
+ return o;
+}
+
+static void free_func(gpointer o)
+{
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+
+ if (data->client) {
+/*
+ ob_debug("button %d focusable %d context %d %d %d\n",
+ data->button, client_mouse_focusable(data->client),
+ data->context,
+ OB_FRAME_CONTEXT_CLIENT, OB_FRAME_CONTEXT_FRAME);
+*/
+ if (data->button == 0 || client_mouse_focusable(data->client) ||
+ (data->context != OB_FRAME_CONTEXT_CLIENT &&
+ data->context != OB_FRAME_CONTEXT_FRAME))
+ {
+ if (o->stop_int)
+ actions_interactive_cancel_act();
+
+ actions_client_move(data, TRUE);
+ client_activate(data->client, TRUE, o->here, FALSE, FALSE, TRUE);
+ actions_client_move(data, FALSE);
+ }
+ } else if (data->context == OB_FRAME_CONTEXT_DESKTOP) {
+ if (o->stop_int)
+ actions_interactive_cancel_act();
+
+ /* focus action on the root window. make keybindings work for this
+ openbox instance, but don't focus any specific client */
+ focus_nothing();
+ }
+
+ return FALSE;
+}
diff --git a/openbox/actions/focustobottom.c b/openbox/actions/focustobottom.c
new file mode 100644
index 0000000..a3e5b5a
--- /dev/null
+++ b/openbox/actions/focustobottom.c
@@ -0,0 +1,17 @@
+#include "openbox/actions.h"
+#include "openbox/focus.h"
+
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_focustobottom_startup(void)
+{
+ actions_register("FocusToBottom", NULL, NULL, run_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ if (data->client)
+ focus_order_to_bottom(data->client);
+ return FALSE;
+}
diff --git a/openbox/actions/fullscreen.c b/openbox/actions/fullscreen.c
new file mode 100644
index 0000000..e1fdf23
--- /dev/null
+++ b/openbox/actions/fullscreen.c
@@ -0,0 +1,20 @@
+#include "openbox/actions.h"
+#include "openbox/client.h"
+
+static gboolean run_func_toggle(ObActionsData *data, gpointer options);
+
+void action_fullscreen_startup(void)
+{
+ actions_register("ToggleFullscreen", NULL, NULL, run_func_toggle);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func_toggle(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ client_fullscreen(data->client, !data->client->fullscreen);
+ actions_client_move(data, FALSE);
+ }
+ return FALSE;
+}
diff --git a/openbox/actions/growtoedge.c b/openbox/actions/growtoedge.c
new file mode 100644
index 0000000..d5a7bfd
--- /dev/null
+++ b/openbox/actions/growtoedge.c
@@ -0,0 +1,200 @@
+#include "openbox/actions.h"
+#include "openbox/misc.h"
+#include "openbox/client.h"
+#include "openbox/frame.h"
+#include "openbox/screen.h"
+#include <glib.h>
+
+typedef struct {
+ ObDirection dir;
+ gboolean shrink;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static gpointer setup_shrink_func(xmlNodePtr node);
+static void free_func(gpointer o);
+static gboolean run_func(ObActionsData *data, gpointer options);
+/* 3.4-compatibility */
+static gpointer setup_north_func(xmlNodePtr node);
+static gpointer setup_south_func(xmlNodePtr node);
+static gpointer setup_east_func(xmlNodePtr node);
+static gpointer setup_west_func(xmlNodePtr node);
+
+void action_growtoedge_startup(void)
+{
+ actions_register("GrowToEdge", setup_func,
+ free_func, run_func);
+ actions_register("ShrinkToEdge", setup_shrink_func,
+ free_func, run_func);
+ /* 3.4-compatibility */
+ actions_register("GrowToEdgeNorth", setup_north_func, free_func, run_func);
+ actions_register("GrowToEdgeSouth", setup_south_func, free_func, run_func);
+ actions_register("GrowToEdgeEast", setup_east_func, free_func, run_func);
+ actions_register("GrowToEdgeWest", setup_west_func, free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+ o->dir = OB_DIRECTION_NORTH;
+ o->shrink = FALSE;
+
+ if ((n = obt_xml_find_node(node, "direction"))) {
+ gchar *s = obt_xml_node_string(n);
+ if (!g_ascii_strcasecmp(s, "north") ||
+ !g_ascii_strcasecmp(s, "up"))
+ o->dir = OB_DIRECTION_NORTH;
+ else if (!g_ascii_strcasecmp(s, "south") ||
+ !g_ascii_strcasecmp(s, "down"))
+ o->dir = OB_DIRECTION_SOUTH;
+ else if (!g_ascii_strcasecmp(s, "west") ||
+ !g_ascii_strcasecmp(s, "left"))
+ o->dir = OB_DIRECTION_WEST;
+ else if (!g_ascii_strcasecmp(s, "east") ||
+ !g_ascii_strcasecmp(s, "right"))
+ o->dir = OB_DIRECTION_EAST;
+ g_free(s);
+ }
+
+ return o;
+}
+
+static gpointer setup_shrink_func(xmlNodePtr node)
+{
+ Options *o;
+
+ o = setup_func(node);
+ o->shrink = TRUE;
+
+ return o;
+}
+
+static gboolean do_grow(ObActionsData *data, gint x, gint y, gint w, gint h)
+{
+ gint realw, realh, lw, lh;
+
+ realw = w;
+ realh = h;
+ client_try_configure(data->client, &x, &y, &realw, &realh,
+ &lw, &lh, TRUE);
+ /* if it's going to be resized smaller than it intended, don't
+ move the window over */
+ if (x != data->client->area.x) x += w - realw;
+ if (y != data->client->area.y) y += h - realh;
+
+ if (x != data->client->area.x || y != data->client->area.y ||
+ realw != data->client->area.width ||
+ realh != data->client->area.height)
+ {
+ actions_client_move(data, TRUE);
+ client_move_resize(data->client, x, y, realw, realh);
+ actions_client_move(data, FALSE);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void free_func(gpointer o)
+{
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+ gint x, y, w, h;
+ ObDirection opp;
+ gint half;
+
+ if (!data->client ||
+ /* don't allow vertical resize if shaded */
+ ((o->dir == OB_DIRECTION_NORTH || o->dir == OB_DIRECTION_SOUTH) &&
+ data->client->shaded))
+ {
+ return FALSE;
+ }
+
+ if (!o->shrink) {
+ /* try grow */
+ client_find_resize_directional(data->client, o->dir, TRUE,
+ &x, &y, &w, &h);
+ if (do_grow(data, x, y, w, h))
+ return FALSE;
+ }
+
+ /* we couldn't grow, so try shrink! */
+ opp = (o->dir == OB_DIRECTION_NORTH ? OB_DIRECTION_SOUTH :
+ (o->dir == OB_DIRECTION_SOUTH ? OB_DIRECTION_NORTH :
+ (o->dir == OB_DIRECTION_EAST ? OB_DIRECTION_WEST :
+ OB_DIRECTION_EAST)));
+ client_find_resize_directional(data->client, opp, FALSE,
+ &x, &y, &w, &h);
+ switch (opp) {
+ case OB_DIRECTION_NORTH:
+ half = data->client->area.y + data->client->area.height / 2;
+ if (y > half) {
+ h += y - half;
+ y = half;
+ }
+ break;
+ case OB_DIRECTION_SOUTH:
+ half = data->client->area.height / 2;
+ if (h < half)
+ h = half;
+ break;
+ case OB_DIRECTION_WEST:
+ half = data->client->area.x + data->client->area.width / 2;
+ if (x > half) {
+ w += x - half;
+ x = half;
+ }
+ break;
+ case OB_DIRECTION_EAST:
+ half = data->client->area.width / 2;
+ if (w < half)
+ w = half;
+ break;
+ default: g_assert_not_reached();
+ }
+ if (do_grow(data, x, y, w, h))
+ return FALSE;
+
+ return FALSE;
+}
+
+/* 3.4-compatibility */
+static gpointer setup_north_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->shrink = FALSE;
+ o->dir = OB_DIRECTION_NORTH;
+ return o;
+}
+
+static gpointer setup_south_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->shrink = FALSE;
+ o->dir = OB_DIRECTION_SOUTH;
+ return o;
+}
+
+static gpointer setup_east_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->shrink = FALSE;
+ o->dir = OB_DIRECTION_EAST;
+ return o;
+}
+
+static gpointer setup_west_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->shrink = FALSE;
+ o->dir = OB_DIRECTION_WEST;
+ return o;
+}
diff --git a/openbox/actions/iconify.c b/openbox/actions/iconify.c
new file mode 100644
index 0000000..e6bdbb7
--- /dev/null
+++ b/openbox/actions/iconify.c
@@ -0,0 +1,23 @@
+#include "openbox/actions.h"
+#include "openbox/client.h"
+
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_iconify_startup(void)
+{
+ actions_register("Iconify",
+ NULL, NULL,
+ run_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ client_iconify(data->client, TRUE, TRUE, FALSE);
+ actions_client_move(data, FALSE);
+ }
+
+ return FALSE;
+}
diff --git a/openbox/actions/if.c b/openbox/actions/if.c
new file mode 100644
index 0000000..0e055a9
--- /dev/null
+++ b/openbox/actions/if.c
@@ -0,0 +1,213 @@
+#include "openbox/actions.h"
+#include "openbox/misc.h"
+#include "openbox/client.h"
+#include "openbox/frame.h"
+#include "openbox/screen.h"
+#include "openbox/focus.h"
+#include <glib.h>
+
+typedef struct {
+ gboolean shaded_on;
+ gboolean shaded_off;
+ gboolean maxvert_on;
+ gboolean maxvert_off;
+ gboolean maxhorz_on;
+ gboolean maxhorz_off;
+ gboolean maxfull_on;
+ gboolean maxfull_off;
+ gboolean iconic_on;
+ gboolean iconic_off;
+ gboolean focused;
+ gboolean unfocused;
+ gboolean urgent_on;
+ gboolean urgent_off;
+ gboolean decor_off;
+ gboolean decor_on;
+ gboolean omnipresent_on;
+ gboolean omnipresent_off;
+ gboolean desktop_current;
+ gboolean desktop_other;
+ guint desktop_number;
+ GPatternSpec *matchtitle;
+ GSList *thenacts;
+ GSList *elseacts;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static void free_func(gpointer options);
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_if_startup(void)
+{
+ actions_register("If", setup_func, free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+
+ if ((n = obt_xml_find_node(node, "shaded"))) {
+ if (obt_xml_node_bool(n))
+ o->shaded_on = TRUE;
+ else
+ o->shaded_off = TRUE;
+ }
+ if ((n = obt_xml_find_node(node, "maximized"))) {
+ if (obt_xml_node_bool(n))
+ o->maxfull_on = TRUE;
+ else
+ o->maxfull_off = TRUE;
+ }
+ if ((n = obt_xml_find_node(node, "maximizedhorizontal"))) {
+ if (obt_xml_node_bool(n))
+ o->maxhorz_on = TRUE;
+ else
+ o->maxhorz_off = TRUE;
+ }
+ if ((n = obt_xml_find_node(node, "maximizedvertical"))) {
+ if (obt_xml_node_bool(n))
+ o->maxvert_on = TRUE;
+ else
+ o->maxvert_off = TRUE;
+ }
+ if ((n = obt_xml_find_node(node, "iconified"))) {
+ if (obt_xml_node_bool(n))
+ o->iconic_on = TRUE;
+ else
+ o->iconic_off = TRUE;
+ }
+ if ((n = obt_xml_find_node(node, "focused"))) {
+ if (obt_xml_node_bool(n))
+ o->focused = TRUE;
+ else
+ o->unfocused = TRUE;
+ }
+ if ((n = obt_xml_find_node(node, "urgent"))) {
+ if (obt_xml_node_bool(n))
+ o->urgent_on = TRUE;
+ else
+ o->urgent_off = TRUE;
+ }
+ if ((n = obt_xml_find_node(node, "undecorated"))) {
+ if (obt_xml_node_bool(n))
+ o->decor_off = TRUE;
+ else
+ o->decor_on = TRUE;
+ }
+ if ((n = obt_xml_find_node(node, "desktop"))) {
+ gchar *s;
+ if ((s = obt_xml_node_string(n))) {
+ if (!g_ascii_strcasecmp(s, "current"))
+ o->desktop_current = TRUE;
+ if (!g_ascii_strcasecmp(s, "other"))
+ o->desktop_other = TRUE;
+ else
+ o->desktop_number = atoi(s);
+ g_free(s);
+ }
+ }
+ if ((n = obt_xml_find_node(node, "omnipresent"))) {
+ if (obt_xml_node_bool(n))
+ o->omnipresent_on = TRUE;
+ else
+ o->omnipresent_off = TRUE;
+ }
+ if ((n = obt_xml_find_node(node, "title"))) {
+ gchar *s;
+ if ((s = obt_xml_node_string(n))) {
+ o->matchtitle = g_pattern_spec_new(s);
+ g_free(s);
+ }
+ }
+
+ if ((n = obt_xml_find_node(node, "then"))) {
+ xmlNodePtr m;
+
+ m = obt_xml_find_node(n->children, "action");
+ while (m) {
+ ObActionsAct *action = actions_parse(m);
+ if (action) o->thenacts = g_slist_append(o->thenacts, action);
+ m = obt_xml_find_node(m->next, "action");
+ }
+ }
+ if ((n = obt_xml_find_node(node, "else"))) {
+ xmlNodePtr m;
+
+ m = obt_xml_find_node(n->children, "action");
+ while (m) {
+ ObActionsAct *action = actions_parse(m);
+ if (action) o->elseacts = g_slist_append(o->elseacts, action);
+ m = obt_xml_find_node(m->next, "action");
+ }
+ }
+
+ return o;
+}
+
+static void free_func(gpointer options)
+{
+ Options *o = options;
+
+ while (o->thenacts) {
+ actions_act_unref(o->thenacts->data);
+ o->thenacts = g_slist_delete_link(o->thenacts, o->thenacts);
+ }
+ while (o->elseacts) {
+ actions_act_unref(o->elseacts->data);
+ o->elseacts = g_slist_delete_link(o->elseacts, o->elseacts);
+ }
+ if (o->matchtitle)
+ g_pattern_spec_free(o->matchtitle);
+
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+ GSList *acts;
+ ObClient *c = data->client;
+
+ if (c &&
+ (!o->shaded_on || c->shaded) &&
+ (!o->shaded_off || !c->shaded) &&
+ (!o->iconic_on || c->iconic) &&
+ (!o->iconic_off || !c->iconic) &&
+ (!o->maxhorz_on || c->max_horz) &&
+ (!o->maxhorz_off || !c->max_horz) &&
+ (!o->maxvert_on || c->max_vert) &&
+ (!o->maxvert_off || !c->max_vert) &&
+ (!o->maxfull_on || (c->max_vert && c->max_horz)) &&
+ (!o->maxfull_off || !(c->max_vert && c->max_horz)) &&
+ (!o->focused || (c == focus_client)) &&
+ (!o->unfocused || !(c == focus_client)) &&
+ (!o->urgent_on || (c->urgent || c->demands_attention)) &&
+ (!o->urgent_off || !(c->urgent || c->demands_attention)) &&
+ (!o->decor_off || (c->undecorated || !(c->decorations & OB_FRAME_DECOR_TITLEBAR))) &&
+ (!o->decor_on || (!c->undecorated && (c->decorations & OB_FRAME_DECOR_TITLEBAR))) &&
+ (!o->omnipresent_on || (c->desktop == DESKTOP_ALL)) &&
+ (!o->omnipresent_off || (c->desktop != DESKTOP_ALL)) &&
+ (!o->desktop_current || ((c->desktop == screen_desktop) ||
+ (c->desktop == DESKTOP_ALL))) &&
+ (!o->desktop_other || ((c->desktop != screen_desktop) &&
+ (c->desktop != DESKTOP_ALL))) &&
+ (!o->desktop_number || ((c->desktop == o->desktop_number - 1) ||
+ (c->desktop == DESKTOP_ALL))) &&
+ (!o->matchtitle ||
+ (g_pattern_match_string(o->matchtitle, c->original_title))))
+ {
+ acts = o->thenacts;
+ }
+ else
+ acts = o->elseacts;
+
+ actions_run_acts(acts, data->uact, data->state,
+ data->x, data->y, data->button,
+ data->context, data->client);
+
+ return FALSE;
+}
diff --git a/openbox/actions/kill.c b/openbox/actions/kill.c
new file mode 100644
index 0000000..b7d547b
--- /dev/null
+++ b/openbox/actions/kill.c
@@ -0,0 +1,20 @@
+#include "openbox/actions.h"
+#include "openbox/client.h"
+
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_kill_startup(void)
+{
+ actions_register("Kill",
+ NULL, NULL,
+ run_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ if (data->client)
+ client_kill(data->client);
+
+ return FALSE;
+}
diff --git a/openbox/actions/layer.c b/openbox/actions/layer.c
new file mode 100644
index 0000000..ed1eeed
--- /dev/null
+++ b/openbox/actions/layer.c
@@ -0,0 +1,132 @@
+#include "openbox/actions.h"
+#include "openbox/client.h"
+
+typedef struct {
+ gint layer; /*!< -1 for below, 0 for normal, and 1 for above */
+ gboolean toggle;
+} Options;
+
+static gpointer setup_func_top(xmlNodePtr node);
+static gpointer setup_func_bottom(xmlNodePtr node);
+static gpointer setup_func_send(xmlNodePtr node);
+static void free_func(gpointer o);
+static gboolean run_func(ObActionsData *data, gpointer options);
+/* 3.4-compatibility */
+static gpointer setup_sendtop_func(xmlNodePtr node);
+static gpointer setup_sendbottom_func(xmlNodePtr node);
+static gpointer setup_sendnormal_func(xmlNodePtr node);
+
+void action_layer_startup(void)
+{
+ actions_register("ToggleAlwaysOnTop", setup_func_top, free_func,
+ run_func);
+ actions_register("ToggleAlwaysOnBottom", setup_func_bottom, free_func,
+ run_func);
+ actions_register("SendToLayer", setup_func_send, free_func,
+ run_func);
+ /* 3.4-compatibility */
+ actions_register("SendToTopLayer", setup_sendtop_func, free_func,
+ run_func);
+ actions_register("SendToBottomLayer", setup_sendbottom_func, free_func,
+ run_func);
+ actions_register("SendToNormalLayer", setup_sendnormal_func, free_func,
+ run_func);
+}
+
+static gpointer setup_func_top(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->layer = 1;
+ o->toggle = TRUE;
+ return o;
+}
+
+static gpointer setup_func_bottom(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->layer = -1;
+ o->toggle = TRUE;
+ return o;
+}
+
+static gpointer setup_func_send(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+
+ if ((n = obt_xml_find_node(node, "layer"))) {
+ gchar *s = obt_xml_node_string(n);
+ if (!g_ascii_strcasecmp(s, "above") ||
+ !g_ascii_strcasecmp(s, "top"))
+ o->layer = 1;
+ else if (!g_ascii_strcasecmp(s, "below") ||
+ !g_ascii_strcasecmp(s, "bottom"))
+ o->layer = -1;
+ else if (!g_ascii_strcasecmp(s, "normal") ||
+ !g_ascii_strcasecmp(s, "middle"))
+ o->layer = 0;
+ g_free(s);
+ }
+
+ return o;
+}
+
+static void free_func(gpointer o)
+{
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+
+ if (data->client) {
+ ObClient *c = data->client;
+
+ actions_client_move(data, TRUE);
+
+ if (o->layer < 0) {
+ if (o->toggle || !c->below)
+ client_set_layer(c, c->below ? 0 : -1);
+ }
+ else if (o->layer > 0) {
+ if (o->toggle || !c->above)
+ client_set_layer(c, c->above ? 0 : 1);
+ }
+ else if (c->above || c->below)
+ client_set_layer(c, 0);
+
+ actions_client_move(data, FALSE);
+ }
+
+ return FALSE;
+}
+
+/* 3.4-compatibility */
+static gpointer setup_sendtop_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->layer = 1;
+ o->toggle = FALSE;
+ return o;
+}
+
+static gpointer setup_sendbottom_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->layer = -1;
+ o->toggle = FALSE;
+ return o;
+}
+
+static gpointer setup_sendnormal_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->layer = 0;
+ o->toggle = FALSE;
+ return o;
+}
+
diff --git a/openbox/actions/lower.c b/openbox/actions/lower.c
new file mode 100644
index 0000000..80ca6b8
--- /dev/null
+++ b/openbox/actions/lower.c
@@ -0,0 +1,24 @@
+#include "openbox/actions.h"
+#include "openbox/stacking.h"
+#include "openbox/window.h"
+
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_lower_startup(void)
+{
+ actions_register("Lower",
+ NULL, NULL,
+ run_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ stacking_lower(CLIENT_AS_WINDOW(data->client));
+ actions_client_move(data, FALSE);
+ }
+
+ return FALSE;
+}
diff --git a/openbox/actions/maximize.c b/openbox/actions/maximize.c
new file mode 100644
index 0000000..db7c36b
--- /dev/null
+++ b/openbox/actions/maximize.c
@@ -0,0 +1,140 @@
+#include "openbox/actions.h"
+#include "openbox/client.h"
+
+/* These match the values for client_maximize */
+typedef enum {
+ BOTH = 0,
+ HORZ = 1,
+ VERT = 2
+} MaxDirection;
+
+typedef struct {
+ MaxDirection dir;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static void free_func(gpointer o);
+static gboolean run_func_on(ObActionsData *data, gpointer options);
+static gboolean run_func_off(ObActionsData *data, gpointer options);
+static gboolean run_func_toggle(ObActionsData *data, gpointer options);
+/* 3.4-compatibility */
+static gpointer setup_both_func(xmlNodePtr node);
+static gpointer setup_horz_func(xmlNodePtr node);
+static gpointer setup_vert_func(xmlNodePtr node);
+
+void action_maximize_startup(void)
+{
+ actions_register("Maximize", setup_func, free_func, run_func_on);
+ actions_register("Unmaximize", setup_func, free_func, run_func_off);
+ actions_register("ToggleMaximize", setup_func, free_func, run_func_toggle);
+ /* 3.4-compatibility */
+ actions_register("MaximizeFull", setup_both_func, free_func,
+ run_func_on);
+ actions_register("UnmaximizeFull", setup_both_func, free_func,
+ run_func_off);
+ actions_register("ToggleMaximizeFull", setup_both_func, free_func,
+ run_func_toggle);
+ actions_register("MaximizeHorz", setup_horz_func, free_func,
+ run_func_on);
+ actions_register("UnmaximizeHorz", setup_horz_func, free_func,
+ run_func_off);
+ actions_register("ToggleMaximizeHorz", setup_horz_func, free_func,
+ run_func_toggle);
+ actions_register("MaximizeVert", setup_vert_func, free_func,
+ run_func_on);
+ actions_register("UnmaximizeVert", setup_vert_func, free_func,
+ run_func_off);
+ actions_register("ToggleMaximizeVert", setup_vert_func, free_func,
+ run_func_toggle);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+ o->dir = BOTH;
+
+ if ((n = obt_xml_find_node(node, "direction"))) {
+ gchar *s = obt_xml_node_string(n);
+ if (!g_ascii_strcasecmp(s, "vertical") ||
+ !g_ascii_strcasecmp(s, "vert"))
+ o->dir = VERT;
+ else if (!g_ascii_strcasecmp(s, "horizontal") ||
+ !g_ascii_strcasecmp(s, "horz"))
+ o->dir = HORZ;
+ g_free(s);
+ }
+
+ return o;
+}
+
+static void free_func(gpointer o)
+{
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func_on(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ client_maximize(data->client, TRUE, o->dir);
+ actions_client_move(data, FALSE);
+ }
+ return FALSE;
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func_off(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ client_maximize(data->client, FALSE, o->dir);
+ actions_client_move(data, FALSE);
+ }
+ return FALSE;
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func_toggle(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+ if (data->client) {
+ gboolean toggle;
+ actions_client_move(data, TRUE);
+ toggle = ((o->dir == HORZ && !data->client->max_horz) ||
+ (o->dir == VERT && !data->client->max_vert) ||
+ (o->dir == BOTH &&
+ !(data->client->max_horz && data->client->max_vert)));
+ client_maximize(data->client, toggle, o->dir);
+ actions_client_move(data, FALSE);
+ }
+ return FALSE;
+}
+
+/* 3.4-compatibility */
+static gpointer setup_both_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->dir = BOTH;
+ return o;
+}
+
+static gpointer setup_horz_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->dir = HORZ;
+ return o;
+}
+
+static gpointer setup_vert_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->dir = VERT;
+ return o;
+}
+
diff --git a/openbox/actions/move.c b/openbox/actions/move.c
new file mode 100644
index 0000000..ba8372a
--- /dev/null
+++ b/openbox/actions/move.c
@@ -0,0 +1,28 @@
+#include "openbox/actions.h"
+#include "openbox/moveresize.h"
+#include "obt/prop.h"
+
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_move_startup(void)
+{
+ actions_register("Move",
+ NULL, NULL,
+ run_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ guint32 corner;
+
+ corner = data->button != 0 ?
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) :
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD);
+
+ moveresize_start(data->client, data->x, data->y, data->button, corner);
+ }
+
+ return FALSE;
+}
diff --git a/openbox/actions/moverelative.c b/openbox/actions/moverelative.c
new file mode 100644
index 0000000..b67b5cf
--- /dev/null
+++ b/openbox/actions/moverelative.c
@@ -0,0 +1,84 @@
+#include "openbox/actions.h"
+#include "openbox/client.h"
+#include "openbox/screen.h"
+#include "openbox/frame.h"
+#include "openbox/config.h"
+
+typedef struct {
+ gint x;
+ gint x_denom;
+ gint y;
+ gint y_denom;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static void free_func(gpointer o);
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_moverelative_startup(void)
+{
+ actions_register("MoveRelative", setup_func, free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+ gchar *s;
+
+ o = g_slice_new0(Options);
+
+ if ((n = obt_xml_find_node(node, "x"))) {
+ s = obt_xml_node_string(n);
+ config_parse_relative_number(s, &o->x, &o->x_denom);
+ g_free(s);
+ }
+ if ((n = obt_xml_find_node(node, "y"))) {
+ s = obt_xml_node_string(n);
+ config_parse_relative_number(s, &o->y, &o->y_denom);
+ g_free(s);
+ }
+
+ return o;
+}
+
+static void free_func(gpointer o)
+{
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+
+ if (data->client) {
+ ObClient *c;
+ gint x, y, lw, lh, w, h;
+
+ c = data->client;
+ x = o->x;
+ y = o->y;
+ if (o->x_denom || o->y_denom) {
+ const Rect *carea;
+
+ carea = screen_area(c->desktop, client_monitor(c), NULL);
+ if (o->x_denom)
+ x = (x * carea->width) / o->x_denom;
+ if (o->y_denom)
+ y = (y * carea->height) / o->y_denom;
+ }
+ x = c->area.x + x;
+ y = c->area.y + y;
+ w = c->area.width;
+ h = c->area.height;
+ client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE);
+ client_find_onscreen(c, &x, &y, w, h, FALSE);
+
+ actions_client_move(data, TRUE);
+ client_configure(c, x, y, w, h, TRUE, TRUE, FALSE);
+ actions_client_move(data, FALSE);
+ }
+
+ return FALSE;
+}
diff --git a/openbox/actions/moveresizeto.c b/openbox/actions/moveresizeto.c
new file mode 100644
index 0000000..c23661c
--- /dev/null
+++ b/openbox/actions/moveresizeto.c
@@ -0,0 +1,189 @@
+#include "openbox/actions.h"
+#include "openbox/client.h"
+#include "openbox/screen.h"
+#include "openbox/frame.h"
+#include "openbox/config.h"
+
+enum {
+ CURRENT_MONITOR = -1,
+ ALL_MONITORS = -2,
+ NEXT_MONITOR = -3,
+ PREV_MONITOR = -4
+};
+
+typedef struct {
+ GravityCoord x;
+ GravityCoord y;
+ gint w;
+ gint w_denom;
+ gint h;
+ gint h_denom;
+ gint monitor;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static void free_func(gpointer o);
+static gboolean run_func(ObActionsData *data, gpointer options);
+/* 3.4-compatibility */
+static gpointer setup_center_func(xmlNodePtr node);
+
+void action_moveresizeto_startup(void)
+{
+ actions_register("MoveResizeTo", setup_func, free_func, run_func);
+ /* 3.4-compatibility */
+ actions_register("MoveToCenter", setup_center_func, free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+ o->x.pos = G_MININT;
+ o->y.pos = G_MININT;
+ o->w = G_MININT;
+ o->h = G_MININT;
+ o->monitor = CURRENT_MONITOR;
+
+ if ((n = obt_xml_find_node(node, "x")))
+ config_parse_gravity_coord(n, &o->x);
+
+ if ((n = obt_xml_find_node(node, "y")))
+ config_parse_gravity_coord(n, &o->y);
+
+ if ((n = obt_xml_find_node(node, "width"))) {
+ gchar *s = obt_xml_node_string(n);
+ if (g_ascii_strcasecmp(s, "current") != 0)
+ config_parse_relative_number(s, &o->w, &o->w_denom);
+ g_free(s);
+ }
+ 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);
+ }
+
+ if ((n = obt_xml_find_node(node, "monitor"))) {
+ gchar *s = obt_xml_node_string(n);
+ if (g_ascii_strcasecmp(s, "current") != 0) {
+ if (!g_ascii_strcasecmp(s, "all"))
+ o->monitor = ALL_MONITORS;
+ else if(!g_ascii_strcasecmp(s, "next"))
+ o->monitor = NEXT_MONITOR;
+ else if(!g_ascii_strcasecmp(s, "prev"))
+ o->monitor = PREV_MONITOR;
+ else
+ o->monitor = obt_xml_node_int(n) - 1;
+ }
+ g_free(s);
+ }
+
+ return o;
+}
+
+static void free_func(gpointer o)
+{
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+
+ if (data->client) {
+ Rect *area, *carea;
+ ObClient *c;
+ guint mon, cmon;
+ gint x, y, lw, lh, w, h;
+
+ c = data->client;
+ mon = o->monitor;
+ cmon = client_monitor(c);
+ switch (mon) {
+ case CURRENT_MONITOR:
+ mon = cmon; break;
+ case ALL_MONITORS:
+ mon = SCREEN_AREA_ALL_MONITORS; break;
+ case NEXT_MONITOR:
+ mon = (cmon + 1 > screen_num_monitors - 1) ? 0 : (cmon + 1); break;
+ case PREV_MONITOR:
+ mon = (cmon == 0) ? (screen_num_monitors - 1) : (cmon - 1); break;
+ default:
+ g_assert_not_reached();
+ }
+
+ area = screen_area(c->desktop, mon, NULL);
+ carea = screen_area(c->desktop, cmon, NULL);
+
+ w = o->w;
+ if (w == G_MININT) w = c->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;
+ else if (o->h_denom) h = (h * area->height) / o->h_denom;
+
+ /* it might not be able to resize how they requested, so find out what
+ it will actually be resized to */
+ x = c->area.x;
+ y = c->area.y;
+ client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE);
+
+ /* get the frame's size */
+ w += c->frame->size.left + c->frame->size.right;
+ h += c->frame->size.top + c->frame->size.bottom;
+
+ x = o->x.pos;
+ if (o->x.denom)
+ x = (x * area->width) / o->x.denom;
+ if (o->x.center) x = (area->width - w) / 2;
+ else if (x == G_MININT) x = c->frame->area.x - carea->x;
+ else if (o->x.opposite) x = area->width - w - x;
+ x += area->x;
+
+ y = o->y.pos;
+ if (o->y.denom)
+ y = (y * area->height) / o->y.denom;
+ if (o->y.center) y = (area->height - h) / 2;
+ else if (y == G_MININT) y = c->frame->area.y - carea->y;
+ else if (o->y.opposite) y = area->height - h - y;
+ y += area->y;
+
+ /* get the client's size back */
+ w -= c->frame->size.left + c->frame->size.right;
+ h -= c->frame->size.top + c->frame->size.bottom;
+
+ frame_frame_gravity(c->frame, &x, &y); /* get the client coords */
+ client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE);
+ /* force it on screen if its moving to another monitor */
+ client_find_onscreen(c, &x, &y, w, h, mon != cmon);
+
+ actions_client_move(data, TRUE);
+ client_configure(c, x, y, w, h, TRUE, TRUE, FALSE);
+ actions_client_move(data, FALSE);
+
+ g_slice_free(Rect, area);
+ g_slice_free(Rect, carea);
+ }
+
+ return FALSE;
+}
+
+/* 3.4-compatibility */
+static gpointer setup_center_func(xmlNodePtr node)
+{
+ Options *o;
+
+ o = g_slice_new0(Options);
+ o->x.pos = G_MININT;
+ o->y.pos = G_MININT;
+ o->w = G_MININT;
+ o->h = G_MININT;
+ o->monitor = -1;
+ o->x.center = TRUE;
+ o->y.center = TRUE;
+ return o;
+}
diff --git a/openbox/actions/movetoedge.c b/openbox/actions/movetoedge.c
new file mode 100644
index 0000000..ef5b692
--- /dev/null
+++ b/openbox/actions/movetoedge.c
@@ -0,0 +1,111 @@
+#include "openbox/actions.h"
+#include "openbox/misc.h"
+#include "openbox/client.h"
+#include "openbox/frame.h"
+#include "openbox/geom.h"
+#include <glib.h>
+
+typedef struct {
+ ObDirection dir;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static void free_func(gpointer o);
+static gboolean run_func(ObActionsData *data, gpointer options);
+/* 3.4-compatibility */
+static gpointer setup_north_func(xmlNodePtr node);
+static gpointer setup_south_func(xmlNodePtr node);
+static gpointer setup_east_func(xmlNodePtr node);
+static gpointer setup_west_func(xmlNodePtr node);
+
+void action_movetoedge_startup(void)
+{
+ actions_register("MoveToEdge", setup_func, free_func, run_func);
+ /* 3.4-compatibility */
+ actions_register("MoveToEdgeNorth", setup_north_func, free_func, run_func);
+ actions_register("MoveToEdgeSouth", setup_south_func, free_func, run_func);
+ actions_register("MoveToEdgeEast", setup_east_func, free_func, run_func);
+ actions_register("MoveToEdgeWest", setup_west_func, free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+ o->dir = OB_DIRECTION_NORTH;
+
+ if ((n = obt_xml_find_node(node, "direction"))) {
+ gchar *s = obt_xml_node_string(n);
+ if (!g_ascii_strcasecmp(s, "north") ||
+ !g_ascii_strcasecmp(s, "up"))
+ o->dir = OB_DIRECTION_NORTH;
+ else if (!g_ascii_strcasecmp(s, "south") ||
+ !g_ascii_strcasecmp(s, "down"))
+ o->dir = OB_DIRECTION_SOUTH;
+ else if (!g_ascii_strcasecmp(s, "west") ||
+ !g_ascii_strcasecmp(s, "left"))
+ o->dir = OB_DIRECTION_WEST;
+ else if (!g_ascii_strcasecmp(s, "east") ||
+ !g_ascii_strcasecmp(s, "right"))
+ o->dir = OB_DIRECTION_EAST;
+ g_free(s);
+ }
+
+ return o;
+}
+
+static void free_func(gpointer o)
+{
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+
+ if (data->client) {
+ gint x, y;
+
+ client_find_move_directional(data->client, o->dir, &x, &y);
+ if (x != data->client->area.x || y != data->client->area.y) {
+ actions_client_move(data, TRUE);
+ client_move(data->client, x, y);
+ actions_client_move(data, FALSE);
+ }
+ }
+
+ return FALSE;
+}
+
+/* 3.4-compatibility */
+static gpointer setup_north_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->dir = OB_DIRECTION_NORTH;
+ return o;
+}
+
+static gpointer setup_south_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->dir = OB_DIRECTION_SOUTH;
+ return o;
+}
+
+static gpointer setup_east_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->dir = OB_DIRECTION_EAST;
+ return o;
+}
+
+static gpointer setup_west_func(xmlNodePtr node)
+{
+ Options *o = g_slice_new0(Options);
+ o->dir = OB_DIRECTION_WEST;
+ return o;
+}
+
diff --git a/openbox/actions/omnipresent.c b/openbox/actions/omnipresent.c
new file mode 100644
index 0000000..4309acc
--- /dev/null
+++ b/openbox/actions/omnipresent.c
@@ -0,0 +1,23 @@
+#include "openbox/actions.h"
+#include "openbox/client.h"
+#include "openbox/screen.h"
+
+static gboolean run_func_toggle(ObActionsData *data, gpointer options);
+
+void action_omnipresent_startup(void)
+{
+ actions_register("ToggleOmnipresent", NULL, NULL, run_func_toggle);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func_toggle(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ client_set_desktop(data->client,
+ data->client->desktop == DESKTOP_ALL ?
+ screen_desktop : DESKTOP_ALL, FALSE, TRUE);
+ actions_client_move(data, FALSE);
+ }
+ return FALSE;
+}
diff --git a/openbox/actions/raise.c b/openbox/actions/raise.c
new file mode 100644
index 0000000..f6ac145
--- /dev/null
+++ b/openbox/actions/raise.c
@@ -0,0 +1,22 @@
+#include "openbox/actions.h"
+#include "openbox/stacking.h"
+#include "openbox/window.h"
+
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_raise_startup(void)
+{
+ actions_register("Raise", NULL, NULL, run_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ stacking_raise(CLIENT_AS_WINDOW(data->client));
+ actions_client_move(data, FALSE);
+ }
+
+ return FALSE;
+}
diff --git a/openbox/actions/raiselower.c b/openbox/actions/raiselower.c
new file mode 100644
index 0000000..dbe41d8
--- /dev/null
+++ b/openbox/actions/raiselower.c
@@ -0,0 +1,21 @@
+#include "openbox/actions.h"
+#include "openbox/stacking.h"
+
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_raiselower_startup(void)
+{
+ actions_register("RaiseLower", NULL, NULL, run_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ stacking_restack_request(data->client, NULL, Opposite);
+ actions_client_move(data, FALSE);
+ }
+
+ return FALSE;
+}
diff --git a/openbox/actions/reconfigure.c b/openbox/actions/reconfigure.c
new file mode 100644
index 0000000..813a122
--- /dev/null
+++ b/openbox/actions/reconfigure.c
@@ -0,0 +1,17 @@
+#include "openbox/actions.h"
+#include "openbox/openbox.h"
+
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_reconfigure_startup(void)
+{
+ actions_register("Reconfigure", NULL, NULL, run_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ ob_reconfigure();
+
+ return FALSE;
+}
diff --git a/openbox/actions/resize.c b/openbox/actions/resize.c
new file mode 100644
index 0000000..f6858d2
--- /dev/null
+++ b/openbox/actions/resize.c
@@ -0,0 +1,198 @@
+#include "openbox/actions.h"
+#include "openbox/moveresize.h"
+#include "openbox/client.h"
+#include "openbox/frame.h"
+#include "obt/prop.h"
+
+typedef struct {
+ gboolean corner_specified;
+ guint32 corner;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static void free_func(gpointer o);
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
+ gboolean shaded);
+
+void action_resize_startup(void)
+{
+ actions_register("Resize", setup_func, free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+
+ if ((n = obt_xml_find_node(node, "edge"))) {
+ gchar *s = obt_xml_node_string(n);
+
+ o->corner_specified = TRUE;
+ if (!g_ascii_strcasecmp(s, "top"))
+ o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP);
+ else if (!g_ascii_strcasecmp(s, "bottom"))
+ o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM);
+ else if (!g_ascii_strcasecmp(s, "left"))
+ o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT);
+ else if (!g_ascii_strcasecmp(s, "right"))
+ o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT);
+ else if (!g_ascii_strcasecmp(s, "topleft"))
+ o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT);
+ else if (!g_ascii_strcasecmp(s, "topright"))
+ o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT);
+ else if (!g_ascii_strcasecmp(s, "bottomleft"))
+ o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT);
+ else if (!g_ascii_strcasecmp(s, "bottomright"))
+ o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT);
+ else
+ o->corner_specified = FALSE;
+
+ g_free(s);
+ }
+ return o;
+}
+
+static void free_func(gpointer o)
+{
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+
+ if (data->client) {
+ ObClient *c = data->client;
+ guint32 corner;
+
+ if (!data->button)
+ corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD);
+ else if (o->corner_specified)
+ corner = o->corner; /* it was specified in the binding */
+ else
+ corner = pick_corner(data->x, data->y,
+ c->frame->area.x, c->frame->area.y,
+ /* use the client size because the frame
+ can be differently sized (shaded
+ windows) and we want this based on the
+ clients size */
+ c->area.width + c->frame->size.left +
+ c->frame->size.right,
+ c->area.height + c->frame->size.top +
+ c->frame->size.bottom, c->shaded);
+
+ moveresize_start(c, data->x, data->y, data->button, corner);
+ }
+
+ return FALSE;
+}
+
+static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch,
+ gboolean shaded)
+{
+ /* let's make x and y client relative instead of screen relative */
+ x = x - cx;
+ y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */
+
+#define X x*ch/cw
+#define A -4*X + 7*ch/3
+#define B 4*X -15*ch/9
+#define C -X/4 + 2*ch/3
+#define D X/4 + 5*ch/12
+#define E X/4 + ch/3
+#define F -X/4 + 7*ch/12
+#define G 4*X - 4*ch/3
+#define H -4*X + 8*ch/3
+#define a (y > 5*ch/9)
+#define b (x < 4*cw/9)
+#define c (x > 5*cw/9)
+#define d (y < 4*ch/9)
+
+ /*
+ Each of these defines (except X which is just there for fun), represents
+ the equation of a line. The lines they represent are shown in the diagram
+ below. Checking y against these lines, we are able to choose a region
+ of the window as shown.
+
+ +---------------------A-------|-------|-------B---------------------+
+ | |A B| |
+ | |A | | B| |
+ | | A B | |
+ | | A | | B | |
+ | | A B | |
+ | | A | | B | |
+ | northwest | A north B | northeast |
+ | | A | | B | |
+ | | A B | |
+ C---------------------+----A--+-------+--B----+---------------------D
+ |CCCCCCC | A B | DDDDDDD|
+ | CCCCCCCC | A | | B | DDDDDDDD |
+ | CCCCCCC A B DDDDDDD |
+ - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - -
+ | | b c | | sh
+ | west | b move c | east | ad
+ | | b c | | ed
+ - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - - -
+ | EEEEEEE G H FFFFFFF |
+ | EEEEEEEE | G | | H | FFFFFFFF |
+ |EEEEEEE | G H | FFFFFFF|
+ E---------------------+----G--+-------+--H----+---------------------F
+ | | G H | |
+ | | G | | H | |
+ | southwest | G south H | southeast |
+ | | G | | H | |
+ | | G H | |
+ | | G | | H | |
+ | | G H | |
+ | |G | | H| |
+ | |G H| |
+ +---------------------G-------|-------|-------H---------------------+
+ */
+
+ if (shaded) {
+ /* for shaded windows, you can only resize west/east and move */
+ if (b)
+ return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT);
+ if (c)
+ return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT);
+ return OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE);
+ }
+
+ if (y < A && y >= C)
+ return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT);
+ else if (y >= A && y >= B && a)
+ return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP);
+ else if (y < B && y >= D)
+ return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT);
+ else if (y < C && y >= E && b)
+ return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT);
+ else if (y < D && y >= F && c)
+ return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT);
+ else if (y < E && y >= G)
+ return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT);
+ else if (y < G && y < H && d)
+ return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM);
+ else if (y >= H && y < F)
+ return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT);
+ else
+ return OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE);
+
+#undef X
+#undef A
+#undef B
+#undef C
+#undef D
+#undef E
+#undef F
+#undef G
+#undef H
+#undef a
+#undef b
+#undef c
+#undef d
+}
diff --git a/openbox/actions/resizerelative.c b/openbox/actions/resizerelative.c
new file mode 100644
index 0000000..e32aff3
--- /dev/null
+++ b/openbox/actions/resizerelative.c
@@ -0,0 +1,104 @@
+#include "openbox/actions.h"
+#include "openbox/client.h"
+#include "openbox/screen.h"
+#include "openbox/frame.h"
+#include "openbox/config.h"
+
+typedef struct {
+ gint left;
+ gint left_denom;
+ gint right;
+ gint right_denom;
+ gint top;
+ gint top_denom;
+ gint bottom;
+ gint bottom_denom;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static void free_func(gpointer options);
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_resizerelative_startup(void)
+{
+ actions_register("ResizeRelative", setup_func, free_func, run_func);
+}
+
+static void xml_node_relative(xmlNodePtr n, gint *num, gint *denom)
+{
+ gchar *s;
+
+ s = obt_xml_node_string(n);
+ config_parse_relative_number(s, num, denom);
+ g_free(s);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+
+ if ((n = obt_xml_find_node(node, "left")))
+ xml_node_relative(n, &o->left, &o->left_denom);
+ if ((n = obt_xml_find_node(node, "right")))
+ xml_node_relative(n, &o->right, &o->right_denom);
+ if ((n = obt_xml_find_node(node, "top")) ||
+ (n = obt_xml_find_node(node, "up")))
+ xml_node_relative(n, &o->top, &o->top_denom);
+ if ((n = obt_xml_find_node(node, "bottom")) ||
+ (n = obt_xml_find_node(node, "down")))
+ xml_node_relative(n, &o->bottom, &o->bottom_denom);
+
+ return o;
+}
+
+static void free_func(gpointer o)
+{
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+
+ if (data->client) {
+ ObClient *c = data->client;
+ gint x, y, ow, xoff, nw, oh, yoff, nh, lw, lh;
+ 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;
+ if (o->right_denom)
+ right = (right * c->area.width / c->size_inc.width) / o->right_denom;
+ if (o->top_denom)
+ top = (top * c->area.height / c->size_inc.height) / o->top_denom;
+ if (o->bottom_denom)
+ bottom = (bottom * c->area.height / c->size_inc.height) / o->bottom_denom;
+
+ 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;
+ oh = c->area.height;
+ yoff = -top * c->size_inc.height;
+ nh = oh + bottom * c->size_inc.height
+ + top * c->size_inc.height;
+
+ client_try_configure(c, &x, &y, &nw, &nh, &lw, &lh, TRUE);
+ xoff = xoff == 0 ? 0 :
+ (xoff < 0 ? MAX(xoff, ow-nw) : MIN(xoff, ow-nw));
+ yoff = yoff == 0 ? 0 :
+ (yoff < 0 ? MAX(yoff, oh-nh) : MIN(yoff, oh-nh));
+
+ actions_client_move(data, TRUE);
+ client_move_resize(c, x + xoff, y + yoff, nw, nh);
+ actions_client_move(data, FALSE);
+ }
+
+ return FALSE;
+}
diff --git a/openbox/actions/restart.c b/openbox/actions/restart.c
new file mode 100644
index 0000000..dc9a218
--- /dev/null
+++ b/openbox/actions/restart.c
@@ -0,0 +1,50 @@
+#include "openbox/actions.h"
+#include "openbox/openbox.h"
+#include "obt/paths.h"
+
+typedef struct {
+ gchar *cmd;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static void free_func(gpointer options);
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_restart_startup(void)
+{
+ actions_register("Restart", setup_func, free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+
+ if ((n = obt_xml_find_node(node, "command")) ||
+ (n = obt_xml_find_node(node, "execute")))
+ {
+ gchar *s = obt_xml_node_string(n);
+ o->cmd = obt_paths_expand_tilde(s);
+ g_free(s);
+ }
+ return o;
+}
+
+static void free_func(gpointer options)
+{
+ Options *o = options;
+ g_free(o->cmd);
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+
+ ob_restart_other(o->cmd);
+
+ return FALSE;
+}
diff --git a/openbox/actions/shade.c b/openbox/actions/shade.c
new file mode 100644
index 0000000..502781d
--- /dev/null
+++ b/openbox/actions/shade.c
@@ -0,0 +1,46 @@
+#include "openbox/actions.h"
+#include "openbox/client.h"
+
+static gboolean run_func_on(ObActionsData *data, gpointer options);
+static gboolean run_func_off(ObActionsData *data, gpointer options);
+static gboolean run_func_toggle(ObActionsData *data, gpointer options);
+
+void action_shade_startup(void)
+{
+ actions_register("Shade", NULL, NULL, run_func_on);
+ actions_register("Unshade", NULL, NULL, run_func_off);
+ actions_register("ToggleShade", NULL, NULL, run_func_toggle);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func_on(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ client_shade(data->client, TRUE);
+ actions_client_move(data, FALSE);
+ }
+ return FALSE;
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func_off(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ client_shade(data->client, FALSE);
+ actions_client_move(data, FALSE);
+ }
+ return FALSE;
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func_toggle(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ client_shade(data->client, !data->client->shaded);
+ actions_client_move(data, FALSE);
+ }
+ return FALSE;
+}
diff --git a/openbox/actions/shadelowerraise.c b/openbox/actions/shadelowerraise.c
new file mode 100644
index 0000000..414e281
--- /dev/null
+++ b/openbox/actions/shadelowerraise.c
@@ -0,0 +1,40 @@
+#include "openbox/actions.h"
+#include "openbox/client.h"
+
+static gboolean run_func_sl(ObActionsData *data, gpointer options);
+static gboolean run_func_ur(ObActionsData *data, gpointer options);
+
+void action_shadelowerraise_startup()
+{
+ /* 3.4-compatibility */
+ actions_register("ShadeLower", NULL, NULL, run_func_sl);
+ actions_register("UnshadeRaise", NULL, NULL, run_func_ur);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func_sl(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ if (data->client->shaded)
+ stacking_lower(CLIENT_AS_WINDOW(data->client));
+ else
+ client_shade(data->client, TRUE);
+ actions_client_move(data, FALSE);
+ }
+ return FALSE;
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func_ur(ObActionsData *data, gpointer options)
+{
+ if (data->client) {
+ actions_client_move(data, TRUE);
+ if (data->client->shaded)
+ client_shade(data->client, FALSE);
+ else
+ stacking_raise(CLIENT_AS_WINDOW(data->client));
+ actions_client_move(data, FALSE);
+ }
+ return FALSE;
+}
diff --git a/openbox/actions/showdesktop.c b/openbox/actions/showdesktop.c
new file mode 100644
index 0000000..6dc77d5
--- /dev/null
+++ b/openbox/actions/showdesktop.c
@@ -0,0 +1,17 @@
+#include "openbox/actions.h"
+#include "openbox/screen.h"
+
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_showdesktop_startup(void)
+{
+ actions_register("ToggleShowDesktop", NULL, NULL, run_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ screen_show_desktop(!screen_showing_desktop, NULL);
+
+ return FALSE;
+}
diff --git a/openbox/actions/showmenu.c b/openbox/actions/showmenu.c
new file mode 100644
index 0000000..485a31d
--- /dev/null
+++ b/openbox/actions/showmenu.c
@@ -0,0 +1,47 @@
+#include "openbox/actions.h"
+#include "openbox/menu.h"
+#include <glib.h>
+
+typedef struct {
+ gchar *name;
+} Options;
+
+static gpointer setup_func(xmlNodePtr node);
+static void free_func(gpointer options);
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_showmenu_startup(void)
+{
+ actions_register("ShowMenu", setup_func, free_func, run_func);
+}
+
+static gpointer setup_func(xmlNodePtr node)
+{
+ xmlNodePtr n;
+ Options *o;
+
+ o = g_slice_new0(Options);
+
+ if ((n = obt_xml_find_node(node, "menu")))
+ o->name = obt_xml_node_string(n);
+ return o;
+}
+
+static void free_func(gpointer options)
+{
+ Options *o = options;
+ g_free(o->name);
+ g_slice_free(Options, o);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ Options *o = options;
+
+ /* you cannot call ShowMenu from inside a menu */
+ if (data->uact != OB_USER_ACTION_MENU_SELECTION && o->name)
+ menu_show(o->name, data->x, data->y, data->button != 0, data->client);
+
+ return FALSE;
+}
diff --git a/openbox/actions/unfocus.c b/openbox/actions/unfocus.c
new file mode 100644
index 0000000..3db00ca
--- /dev/null
+++ b/openbox/actions/unfocus.c
@@ -0,0 +1,17 @@
+#include "openbox/actions.h"
+#include "openbox/focus.h"
+
+static gboolean run_func(ObActionsData *data, gpointer options);
+
+void action_unfocus_startup(void)
+{
+ actions_register("Unfocus", NULL, NULL, run_func);
+}
+
+/* Always return FALSE because its not interactive */
+static gboolean run_func(ObActionsData *data, gpointer options)
+{
+ if (data->client && data->client == focus_client)
+ focus_fallback(FALSE, FALSE, TRUE, FALSE);
+ return FALSE;
+}
diff --git a/openbox/client.c b/openbox/client.c
new file mode 100644
index 0000000..f3b4bda
--- /dev/null
+++ b/openbox/client.c
@@ -0,0 +1,4603 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ client.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "client.h"
+#include "debug.h"
+#include "startupnotify.h"
+#include "dock.h"
+#include "screen.h"
+#include "moveresize.h"
+#include "ping.h"
+#include "place.h"
+#include "frame.h"
+#include "session.h"
+#include "event.h"
+#include "grab.h"
+#include "prompt.h"
+#include "focus.h"
+#include "focus_cycle.h"
+#include "stacking.h"
+#include "openbox.h"
+#include "group.h"
+#include "config.h"
+#include "menuframe.h"
+#include "keyboard.h"
+#include "mouse.h"
+#include "obrender/render.h"
+#include "gettext.h"
+#include "obt/display.h"
+#include "obt/xqueue.h"
+#include "obt/prop.h"
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_SIGNAL_H
+# include <signal.h> /* for kill() */
+#endif
+
+#include <glib.h>
+#include <X11/Xutil.h>
+
+/*! The event mask to grab on client windows */
+#define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
+ ColormapChangeMask)
+
+#define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
+ ButtonMotionMask)
+
+typedef struct
+{
+ ObClientCallback func;
+ gpointer data;
+} ClientCallback;
+
+GList *client_list = NULL;
+
+static GSList *client_destroy_notifies = NULL;
+static RrImage *client_default_icon = NULL;
+
+static void client_get_all(ObClient *self, gboolean real);
+static void client_get_startup_id(ObClient *self);
+static void client_get_session_ids(ObClient *self);
+static void client_save_app_rule_values(ObClient *self);
+static void client_get_area(ObClient *self);
+static void client_get_desktop(ObClient *self);
+static void client_get_state(ObClient *self);
+static void client_get_shaped(ObClient *self);
+static void client_get_colormap(ObClient *self);
+static void client_set_desktop_recursive(ObClient *self,
+ guint target,
+ gboolean donthide,
+ gboolean dontraise);
+static void client_change_allowed_actions(ObClient *self);
+static void client_change_state(ObClient *self);
+static void client_change_wm_state(ObClient *self);
+static void client_apply_startup_state(ObClient *self,
+ gint x, gint y, gint w, gint h);
+static void client_restore_session_state(ObClient *self);
+static gboolean client_restore_session_stacking(ObClient *self);
+static ObAppSettings *client_get_settings_state(ObClient *self);
+static void client_update_transient_tree(ObClient *self,
+ ObGroup *oldgroup, ObGroup *newgroup,
+ gboolean oldgtran, gboolean newgtran,
+ ObClient* oldparent,
+ ObClient *newparent);
+static void client_present(ObClient *self, gboolean here, gboolean raise,
+ gboolean unshade);
+static GSList *client_search_all_top_parents_internal(ObClient *self,
+ gboolean bylayer,
+ ObStackingLayer layer);
+static void client_call_notifies(ObClient *self, GSList *list);
+static void client_ping_event(ObClient *self, gboolean dead);
+static void client_prompt_kill(ObClient *self);
+static gboolean client_can_steal_focus(ObClient *self,
+ gboolean allow_other_desktop,
+ gboolean request_from_user,
+ Time steal_time, Time launch_time);
+static void client_setup_default_decor_and_functions(ObClient *self);
+static void client_setup_decor_undecorated(ObClient *self);
+
+void client_startup(gboolean reconfig)
+{
+ client_default_icon = RrImageNewFromData(
+ ob_rr_icons, ob_rr_theme->def_win_icon,
+ ob_rr_theme->def_win_icon_w, ob_rr_theme->def_win_icon_h);
+
+ if (reconfig) return;
+
+ client_set_list();
+}
+
+void client_shutdown(gboolean reconfig)
+{
+ RrImageUnref(client_default_icon);
+ client_default_icon = NULL;
+
+ if (reconfig) return;
+}
+
+static void client_call_notifies(ObClient *self, GSList *list)
+{
+ GSList *it;
+
+ for (it = list; it; it = g_slist_next(it)) {
+ ClientCallback *d = it->data;
+ d->func(self, d->data);
+ }
+}
+
+void client_add_destroy_notify(ObClientCallback func, gpointer data)
+{
+ ClientCallback *d = g_slice_new(ClientCallback);
+ d->func = func;
+ d->data = data;
+ client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
+}
+
+void client_remove_destroy_notify(ObClientCallback func)
+{
+ GSList *it;
+
+ for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
+ ClientCallback *d = it->data;
+ if (d->func == func) {
+ 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;
+ GList *it;
+ guint size = g_list_length(client_list);
+
+ /* create an array of the window ids */
+ if (size > 0) {
+ windows = g_new(Window, size);
+ win_it = windows;
+ for (it = client_list; it; it = g_list_next(it), ++win_it)
+ *win_it = ((ObClient*)it->data)->window;
+ } else
+ windows = NULL;
+
+ OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST, WINDOW,
+ (gulong*)windows, size);
+
+ if (windows)
+ g_free(windows);
+
+ stacking_set_list();
+}
+
+void client_manage(Window window, ObPrompt *prompt)
+{
+ ObClient *self;
+ XSetWindowAttributes attrib_set;
+ gboolean try_activate = FALSE;
+ gboolean do_activate;
+ ObAppSettings *settings;
+ gboolean transient = FALSE;
+ Rect place;
+ Time launch_time;
+ guint32 user_time;
+ gboolean obplaced;
+
+ ob_debug("Managing window: 0x%lx", window);
+
+ /* choose the events we want to receive on the CLIENT window
+ (ObPrompt windows can request events too) */
+ attrib_set.event_mask = CLIENT_EVENTMASK |
+ (prompt ? prompt->event_mask : 0);
+ attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
+ XChangeWindowAttributes(obt_display, window,
+ CWEventMask|CWDontPropagate, &attrib_set);
+
+ /* create the ObClient struct, and populate it from the hints on the
+ window */
+ self = g_slice_new0(ObClient);
+ self->obwin.type = OB_WINDOW_CLASS_CLIENT;
+ self->window = window;
+ self->prompt = prompt;
+ self->managed = TRUE;
+
+ /* non-zero defaults */
+ self->wmstate = WithdrawnState; /* make sure it gets updated first time */
+ self->gravity = NorthWestGravity;
+ self->desktop = screen_num_desktops; /* always an invalid value */
+
+ /* get all the stuff off the window */
+ client_get_all(self, TRUE);
+
+ ob_debug("Window type: %d", self->type);
+ 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);
+
+ /* per-app settings override stuff from client_get_all, and return the
+ settings for other uses too. the returned settings is a shallow copy,
+ that needs to be freed with g_free(). */
+ settings = client_get_settings_state(self);
+
+ /* the session should get the last say though */
+ client_restore_session_state(self);
+
+ /* the per-app settings/session may have changed the decorations for
+ the window, so we setup decorations for that here. this is a special
+ case because we want to place the window according to these decoration
+ changes.
+ we do this before setting up the frame so that it will reflect the
+ decorations of the window as it will be placed on screen.
+ */
+ client_setup_decor_undecorated(self);
+
+ /* specify that if we exit, the window should not be destroyed and
+ should be reparented back to root automatically, unless we are managing
+ an internal ObPrompt window */
+ if (!self->prompt)
+ XChangeSaveSet(obt_display, window, SetModeInsert);
+
+ /* create the decoration frame for the client window */
+ self->frame = frame_new(self);
+
+ frame_grab_client(self->frame);
+
+ /* we've grabbed everything and set everything that we need to at mapping
+ time now */
+ grab_server(FALSE);
+
+ /* this needs to occur once we have a frame, since it sets a property on
+ the frame */
+ client_update_opacity(self);
+
+ /* don't put helper/modal windows on a different desktop if they are
+ related to the focused window. */
+ if (!screen_compare_desktops(self->desktop, screen_desktop) &&
+ focus_client && client_search_transient(focus_client, self) &&
+ (client_helper(self) || self->modal))
+ {
+ self->desktop = screen_desktop;
+ }
+
+ /* tell startup notification that this app started */
+ launch_time = sn_app_started(self->startup_id, self->class, self->name);
+
+ if (!OBT_PROP_GET32(self->window, NET_WM_USER_TIME, CARDINAL, &user_time))
+ user_time = event_time();
+
+ /* do this after we have a frame.. it uses the frame to help determine the
+ WM_STATE to apply. */
+ client_change_state(self);
+
+ /* add ourselves to the focus order */
+ focus_order_add_new(self);
+
+ /* do this to add ourselves to the stacking list in a non-intrusive way */
+ client_calc_layer(self);
+
+ /* focus the new window? */
+ if (ob_state() != OB_STATE_STARTING &&
+ (!self->session || self->session->focused) &&
+ /* this means focus=true for window is same as config_focus_new=true */
+ ((config_focus_new || settings->focus == 1) ||
+ client_search_focus_tree_full(self)) &&
+ /* NET_WM_USER_TIME 0 when mapping means don't focus */
+ (user_time != 0) &&
+ /* this checks for focus=false for the window */
+ settings->focus != 0 &&
+ focus_valid_target(self, self->desktop,
+ FALSE, FALSE, TRUE, TRUE, FALSE, FALSE,
+ settings->focus == 1))
+ {
+ try_activate = TRUE;
+ }
+
+ /* remove the client's border */
+ XSetWindowBorderWidth(obt_display, self->window, 0);
+
+ /* adjust the frame to the client's size before showing or placing
+ the window */
+ frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
+ frame_adjust_client_area(self->frame);
+
+ /* where the frame was placed is where the window was originally */
+ place = self->area;
+
+ ob_debug("Going to try activate new window? %s",
+ try_activate ? "yes" : "no");
+ if (try_activate)
+ do_activate = client_can_steal_focus(
+ self, settings->focus == 1,
+ (!!launch_time || settings->focus == 1),
+ event_time(), launch_time);
+ else
+ do_activate = FALSE;
+
+ /* figure out placement for the window if the window is new */
+ if (ob_state() == OB_STATE_RUNNING) {
+ ob_debug("Positioned: %s @ %d %d",
+ (!self->positioned ? "no" :
+ (self->positioned == PPosition ? "program specified" :
+ (self->positioned == USPosition ? "user specified" :
+ (self->positioned == (PPosition | USPosition) ?
+ "program + user specified" :
+ "BADNESS !?")))), place.x, place.y);
+
+ ob_debug("Sized: %s @ %d %d",
+ (!self->sized ? "no" :
+ (self->sized == PSize ? "program specified" :
+ (self->sized == USSize ? "user specified" :
+ (self->sized == (PSize | USSize) ?
+ "program + user specified" :
+ "BADNESS !?")))), place.width, place.height);
+
+ obplaced = place_client(self, do_activate, &place.x, &place.y,
+ settings);
+
+ /* watch for buggy apps that ask to be placed at (0,0) when there is
+ a strut there */
+ if (!obplaced && place.x == 0 && place.y == 0 &&
+ /* non-normal windows are allowed */
+ client_normal(self) &&
+ /* oldschool fullscreen windows are allowed */
+ !client_is_oldfullscreen(self, &place))
+ {
+ Rect *r;
+
+ r = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, NULL);
+ if (r->x || r->y) {
+ place.x = r->x;
+ place.y = r->y;
+ ob_debug("Moving buggy app from (0,0) to (%d,%d)", r->x, r->y);
+ }
+ g_slice_free(Rect, r);
+ }
+
+ /* make sure the window is visible. */
+ client_find_onscreen(self, &place.x, &place.y,
+ place.width, place.height,
+ /* non-normal clients has less rules, and
+ windows that are being restored from a
+ session do also. we can assume you want
+ it back where you saved it. Clients saying
+ they placed themselves are subjected to
+ harder rules, ones that are placed by
+ place.c or by the user are allowed partially
+ off-screen and on xinerama divides (ie,
+ it is up to the placement routines to avoid
+ the xinerama divides)
+
+ children and splash screens are forced on
+ screen, but i don't remember why i decided to
+ do that.
+ */
+ ob_state() == OB_STATE_RUNNING &&
+ (self->type == OB_CLIENT_TYPE_DIALOG ||
+ self->type == OB_CLIENT_TYPE_SPLASH ||
+ (!((self->positioned & USPosition) ||
+ settings->pos_given) &&
+ client_normal(self) &&
+ !self->session &&
+ /* don't move oldschool fullscreen windows to
+ fit inside the struts (fixes Acroread, which
+ makes its fullscreen window fit the screen
+ but it is not USSize'd or USPosition'd) */
+ !client_is_oldfullscreen(self, &place))));
+ }
+
+ /* if the window isn't user-sized, then make it fit inside
+ the visible screen area on its monitor. Use basically the same rules
+ for forcing the window on screen in the client_find_onscreen call.
+
+ do this after place_client, it chooses the monitor!
+
+ splash screens get "transient" set to TRUE by
+ the place_client call
+ */
+ if (ob_state() == OB_STATE_RUNNING &&
+ (transient ||
+ (!(self->sized & USSize || self->positioned & USPosition) &&
+ client_normal(self) &&
+ !self->session &&
+ /* don't shrink oldschool fullscreen windows to fit inside the
+ struts (fixes Acroread, which makes its fullscreen window
+ fit the screen but it is not USSize'd or USPosition'd) */
+ !client_is_oldfullscreen(self, &place))))
+ {
+ Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
+
+ /* get the size of the frame */
+ place.width += self->frame->size.left + self->frame->size.right;
+ place.height += self->frame->size.top + self->frame->size.bottom;
+
+ /* fit the window inside the area */
+ place.width = MIN(place.width, a->width);
+ place.height = MIN(place.height, a->height);
+
+ ob_debug("setting window size to %dx%d", place.width, place.height);
+
+ /* get the size of the client back */
+ place.width -= self->frame->size.left + self->frame->size.right;
+ place.height -= self->frame->size.top + self->frame->size.bottom;
+
+ g_slice_free(Rect, a);
+ }
+
+ ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
+ "some restrictions may apply",
+ self->window, place.x, place.y, place.width, place.height);
+ if (self->session)
+ ob_debug(" but session requested %d, %d %d x %d instead, "
+ "overriding",
+ self->session->x, self->session->y,
+ self->session->w, self->session->h);
+
+ /* do this after the window is placed, so the premax/prefullscreen numbers
+ won't be all wacko!!
+
+ this also places the window
+ */
+ client_apply_startup_state(self, place.x, place.y,
+ place.width, place.height);
+
+ /* set the initial value of the desktop hint, when one wasn't requested
+ on map. */
+ OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
+
+ /* grab mouse bindings before showing the window */
+ mouse_grab_for_client(self, TRUE);
+
+ /* 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);
+ }
+
+ /* activate/hilight/raise the window */
+ if (try_activate) {
+ if (do_activate) {
+ gboolean stacked = client_restore_session_stacking(self);
+ client_present(self, FALSE, !stacked, TRUE);
+ }
+ else {
+ /* if the client isn't stealing focus, then hilite it so the user
+ knows it is there, but don't do this if we're restoring from a
+ session */
+ if (!client_restore_session_stacking(self))
+ client_hilite(self, TRUE);
+ }
+ }
+ else {
+ /* This may look rather odd. Well it's because new windows are added
+ to the stacking order non-intrusively. If we're not going to focus
+ the new window or hilite it, then we raise it to the top. This will
+ take affect for things that don't get focused like splash screens.
+ Also if you don't have focus_new enabled, then it's going to get
+ raised to the top. Legacy begets legacy I guess?
+ */
+ if (!client_restore_session_stacking(self))
+ stacking_raise(CLIENT_AS_WINDOW(self));
+ }
+
+ /* add to client list/map */
+ client_list = g_list_append(client_list, self);
+ window_add(&self->window, CLIENT_AS_WINDOW(self));
+
+ /* this has to happen after we're in the client_list */
+ if (STRUT_EXISTS(self->strut))
+ screen_update_areas();
+
+ /* update the list hints */
+ client_set_list();
+
+ /* free the ObAppSettings shallow copy */
+ g_slice_free(ObAppSettings, settings);
+
+ ob_debug("Managed window 0x%lx plate 0x%x (%s)",
+ window, self->frame->window, self->class);
+}
+
+ObClient *client_fake_manage(Window window)
+{
+ ObClient *self;
+ ObAppSettings *settings;
+
+ ob_debug("Pretend-managing window: %lx", window);
+
+ /* do this minimal stuff to figure out the client's decorations */
+
+ self = g_slice_new0(ObClient);
+ self->window = window;
+
+ client_get_all(self, FALSE);
+ /* per-app settings override stuff, and return the settings for other
+ uses too. this returns a shallow copy that needs to be freed */
+ settings = client_get_settings_state(self);
+
+ /* create the decoration frame for the client window and adjust its size */
+ self->frame = frame_new(self);
+
+ client_apply_startup_state(self, self->area.x, self->area.y,
+ self->area.width, self->area.height);
+
+ ob_debug("gave extents left %d right %d top %d bottom %d",
+ self->frame->size.left, self->frame->size.right,
+ self->frame->size.top, self->frame->size.bottom);
+
+ /* free the ObAppSettings shallow copy */
+ g_slice_free(ObAppSettings, settings);
+
+ return self;
+}
+
+void client_unmanage_all(void)
+{
+ while (client_list)
+ client_unmanage(client_list->data);
+}
+
+void client_unmanage(ObClient *self)
+{
+ GSList *it;
+ gulong ignore_start;
+
+ ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
+ self->window, self->frame->window,
+ self->class, self->title ? self->title : "");
+
+ g_assert(self != NULL);
+
+ /* we dont want events no more. do this before hiding the frame so we
+ don't generate more events */
+ XSelectInput(obt_display, self->window, NoEventMask);
+
+ /* ignore enter events from the unmap so it doesnt mess with the focus */
+ if (!config_focus_under_mouse)
+ ignore_start = event_start_ignore_all_enters();
+
+ frame_hide(self->frame);
+ /* flush to send the hide to the server quickly */
+ XFlush(obt_display);
+
+ if (!config_focus_under_mouse)
+ event_end_ignore_all_enters(ignore_start);
+
+ mouse_grab_for_client(self, FALSE);
+
+ self->managed = FALSE;
+
+ /* remove the window from our save set, unless we are managing an internal
+ ObPrompt window */
+ if (!self->prompt)
+ XChangeSaveSet(obt_display, self->window, SetModeDelete);
+
+ /* update the focus lists */
+ focus_order_remove(self);
+ if (client_focused(self)) {
+ /* don't leave an invalid focus_client */
+ focus_client = NULL;
+ }
+
+ /* if we're prompting to kill the client, close that */
+ prompt_unref(self->kill_prompt);
+ self->kill_prompt = NULL;
+
+ client_list = g_list_remove(client_list, self);
+ stacking_remove(self);
+ window_remove(self->window);
+
+ /* once the client is out of the list, update the struts to remove its
+ influence */
+ if (STRUT_EXISTS(self->strut))
+ screen_update_areas();
+
+ client_call_notifies(self, client_destroy_notifies);
+
+ /* tell our parent(s) that we're gone */
+ for (it = self->parents; it; it = g_slist_next(it))
+ ((ObClient*)it->data)->transients =
+ g_slist_remove(((ObClient*)it->data)->transients,self);
+
+ /* tell our transients that we're gone */
+ for (it = self->transients; it; it = g_slist_next(it)) {
+ ((ObClient*)it->data)->parents =
+ g_slist_remove(((ObClient*)it->data)->parents, self);
+ /* we could be keeping our children in a higher layer */
+ client_calc_layer(it->data);
+ }
+
+ /* remove from its group */
+ if (self->group) {
+ group_remove(self->group, self);
+ self->group = NULL;
+ }
+
+ /* restore the window's original geometry so it is not lost */
+ {
+ Rect a;
+
+ a = self->area;
+
+ if (self->fullscreen)
+ a = self->pre_fullscreen_area;
+ else if (self->max_horz || self->max_vert) {
+ if (self->max_horz) {
+ a.x = self->pre_max_area.x;
+ a.width = self->pre_max_area.width;
+ }
+ if (self->max_vert) {
+ a.y = self->pre_max_area.y;
+ a.height = self->pre_max_area.height;
+ }
+ }
+
+ self->fullscreen = self->max_horz = self->max_vert = FALSE;
+ /* let it be moved and resized no matter what */
+ self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
+ self->decorations = 0; /* unmanaged windows have no decor */
+
+ /* give the client its border back */
+ XSetWindowBorderWidth(obt_display, self->window, self->border_width);
+
+ client_move_resize(self, a.x, a.y, a.width, a.height);
+ }
+
+ /* reparent the window out of the frame, and free the frame */
+ frame_release_client(self->frame);
+ frame_free(self->frame);
+ self->frame = NULL;
+
+ if (ob_state() != OB_STATE_EXITING) {
+ /* these values should not be persisted across a window
+ unmapping/mapping */
+ OBT_PROP_ERASE(self->window, NET_WM_DESKTOP);
+ OBT_PROP_ERASE(self->window, NET_WM_STATE);
+ OBT_PROP_ERASE(self->window, WM_STATE);
+ } else {
+ /* if we're left in an unmapped state, the client wont be mapped.
+ this is bad, since we will no longer be managing the window on
+ restart */
+ XMapWindow(obt_display, self->window);
+ }
+
+ /* these should not be left on the window ever. other window managers
+ don't necessarily use them and it will mess them up (like compiz) */
+ OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_NAME);
+ OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_ICON_NAME);
+
+ /* update the list hints */
+ client_set_list();
+
+ ob_debug("Unmanaged window 0x%lx", self->window);
+
+ /* free all data allocated in the client struct */
+ RrImageUnref(self->icon_set);
+ g_slist_free(self->transients);
+ g_free(self->startup_id);
+ g_free(self->wm_command);
+ g_free(self->title);
+ g_free(self->icon_title);
+ g_free(self->original_title);
+ g_free(self->name);
+ g_free(self->class);
+ g_free(self->role);
+ g_free(self->client_machine);
+ g_free(self->sm_client_id);
+ g_slice_free(ObClient, self);
+}
+
+void client_fake_unmanage(ObClient *self)
+{
+ /* this is all that got allocated to get the decorations */
+
+ frame_free(self->frame);
+ g_slice_free(ObClient, self);
+}
+
+static gboolean client_can_steal_focus(ObClient *self,
+ gboolean allow_other_desktop,
+ gboolean request_from_user,
+ Time steal_time,
+ Time launch_time)
+{
+ gboolean steal;
+ gboolean relative_focused;
+
+ steal = TRUE;
+
+ relative_focused = (focus_client != NULL &&
+ (client_search_focus_tree_full(self) != NULL ||
+ client_search_focus_group_full(self) != NULL));
+
+ /* This is focus stealing prevention */
+ ob_debug("Want to focus window 0x%x at time %u "
+ "launched at %u (last user interaction time %u) "
+ "request from %s, allow other desktop: %s, "
+ "desktop switch time %u",
+ self->window, steal_time, launch_time,
+ event_last_user_time,
+ (request_from_user ? "user" : "other"),
+ (allow_other_desktop ? "yes" : "no"),
+ screen_desktop_user_time);
+
+ /*
+ if no launch time is provided for an application, make one up.
+
+ if the window is related to other existing windows
+ and one of those windows was the last used
+ then we will give it a launch time equal to the last user time,
+ which will end up giving the window focus probably.
+ else
+ the window is related to other windows, but you are not working in
+ them?
+ seems suspicious, so we will give it a launch time of
+ NOW - STEAL_INTERVAL,
+ so it will be given focus only if we didn't use something else
+ during the steal interval.
+ else
+ the window is all on its own, so we can't judge it. give it a launch
+ time equal to the last user time, so it will probably take focus.
+
+ this way running things from a terminal will give them focus, but popups
+ without a launch time shouldn't steal focus so easily.
+ */
+
+ if (!launch_time) {
+ if (client_has_relative(self)) {
+ if (event_last_user_time && client_search_focus_group_full(self)) {
+ /* our relative is focused */
+ launch_time = event_last_user_time;
+ ob_debug("Unknown launch time, using %u - window in active "
+ "group", launch_time);
+ }
+ else if (!request_from_user) {
+ /* has relatives which are not being used. suspicious */
+ launch_time = event_time() - OB_EVENT_USER_TIME_DELAY;
+ ob_debug("Unknown launch time, using %u - window in inactive "
+ "group", launch_time);
+ }
+ else {
+ /* has relatives which are not being used, but the user seems
+ to want to go there! */
+ launch_time = event_last_user_time;
+ ob_debug("Unknown launch time, using %u - user request",
+ launch_time);
+ }
+ }
+ else {
+ /* the window is on its own, probably the user knows it is going
+ to appear */
+ launch_time = event_last_user_time;
+ ob_debug("Unknown launch time, using %u - independent window",
+ launch_time);
+ }
+ }
+
+ /* if it's on another desktop
+ and if allow_other_desktop is true, we generally let it steal focus.
+ but if it didn't come from the user, don't let it steal unless it was
+ launched before the user switched desktops.
+ focus, unless it was launched after we changed desktops and the request
+ came from the user
+ */
+ if (!screen_compare_desktops(screen_desktop, self->desktop)) {
+ /* must be allowed */
+ if (!allow_other_desktop) {
+ steal = FALSE;
+ ob_debug("Not focusing the window because its on another desktop");
+ }
+ /* if we don't know when the desktop changed, but request is from an
+ application, don't let it change desktop on you */
+ else if (!request_from_user) {
+ steal = FALSE;
+ ob_debug("Not focusing the window because non-user request");
+ }
+ }
+ /* If something is focused... */
+ else if (focus_client) {
+ /* If the user is working in another window right now, then don't
+ steal focus */
+ if (!relative_focused &&
+ event_last_user_time &&
+ /* last user time must be strictly > launch_time to block focus */
+ (event_time_after(event_last_user_time, launch_time) &&
+ event_last_user_time != launch_time) &&
+ event_time_after(event_last_user_time,
+ steal_time - OB_EVENT_USER_TIME_DELAY))
+ {
+ steal = FALSE;
+ ob_debug("Not focusing the window because the user is "
+ "working in another window that is not its relative");
+ }
+ /* Don't move focus if it's not going to go to this window
+ anyway */
+ else if (client_focus_target(self) != self) {
+ steal = FALSE;
+ ob_debug("Not focusing the window because another window "
+ "would get the focus anyway");
+ }
+ /* For requests that don't come from the user */
+ else if (!request_from_user) {
+ /* If the new window is a transient (and its relatives aren't
+ focused) */
+ if (client_has_parent(self) && !relative_focused) {
+ steal = FALSE;
+ ob_debug("Not focusing the window because it is a "
+ "transient, and its relatives aren't focused");
+ }
+ /* Don't steal focus from globally active clients.
+ I stole this idea from KWin. It seems nice.
+ */
+ else if (!(focus_client->can_focus || focus_client->focus_notify))
+ {
+ steal = FALSE;
+ ob_debug("Not focusing the window because a globally "
+ "active client has focus");
+ }
+ /* Don't move focus if the window is not visible on the current
+ desktop and none of its relatives are focused */
+ else if (!allow_other_desktop &&
+ !screen_compare_desktops(self->desktop, screen_desktop) &&
+ !relative_focused)
+ {
+ steal = FALSE;
+ ob_debug("Not focusing the window because it is on "
+ "another desktop and no relatives are focused ");
+ }
+ }
+ }
+
+ if (!steal)
+ ob_debug("Focus stealing prevention activated for %s at "
+ "time %u (last user interaction time %u)",
+ self->title, steal_time, event_last_user_time);
+ else
+ ob_debug("Allowing focus stealing for %s at time %u (last user "
+ "interaction time %u)",
+ self->title, steal_time, event_last_user_time);
+ return steal;
+}
+
+/*! Returns a new structure containing the per-app settings for this client.
+ The returned structure needs to be freed with g_free. */
+static ObAppSettings *client_get_settings_state(ObClient *self)
+{
+ ObAppSettings *settings;
+ GSList *it;
+
+ settings = config_create_app_settings();
+
+ for (it = config_per_app_settings; it; it = g_slist_next(it)) {
+ ObAppSettings *app = it->data;
+ gboolean match = TRUE;
+
+ g_assert(app->name != NULL || app->class != NULL ||
+ app->role != NULL || app->title != NULL ||
+ (signed)app->type >= 0);
+
+ if (app->name &&
+ !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
+ match = FALSE;
+ else if (app->class &&
+ !g_pattern_match(app->class,
+ strlen(self->class), self->class, NULL))
+ match = FALSE;
+ else if (app->role &&
+ !g_pattern_match(app->role,
+ strlen(self->role), self->role, NULL))
+ match = FALSE;
+ else if (app->title &&
+ !g_pattern_match(app->title,
+ strlen(self->title), self->title, NULL))
+ match = FALSE;
+ else if ((signed)app->type >= 0 && app->type != self->type) {
+ match = FALSE;
+ }
+
+ if (match) {
+ ob_debug("Window matching: %s", app->name);
+
+ /* copy the settings to our struct, overriding the existing
+ settings if they are not defaults */
+ config_app_settings_copy_non_defaults(app, settings);
+ }
+ }
+
+ if (settings->shade != -1)
+ self->shaded = !!settings->shade;
+ if (settings->decor != -1)
+ self->undecorated = !settings->decor;
+ if (settings->iconic != -1)
+ self->iconic = !!settings->iconic;
+ if (settings->skip_pager != -1)
+ self->skip_pager = !!settings->skip_pager;
+ if (settings->skip_taskbar != -1)
+ self->skip_taskbar = !!settings->skip_taskbar;
+
+ if (settings->max_vert != -1)
+ self->max_vert = !!settings->max_vert;
+ if (settings->max_horz != -1)
+ self->max_horz = !!settings->max_horz;
+
+ if (settings->fullscreen != -1)
+ self->fullscreen = !!settings->fullscreen;
+
+ if (settings->desktop) {
+ if (settings->desktop == DESKTOP_ALL)
+ self->desktop = settings->desktop;
+ else if (settings->desktop > 0 &&
+ settings->desktop <= screen_num_desktops)
+ self->desktop = settings->desktop - 1;
+ }
+
+ if (settings->layer == -1) {
+ self->below = TRUE;
+ self->above = FALSE;
+ }
+ else if (settings->layer == 0) {
+ self->below = FALSE;
+ self->above = FALSE;
+ }
+ else if (settings->layer == 1) {
+ self->below = FALSE;
+ self->above = TRUE;
+ }
+ return settings;
+}
+
+static void client_restore_session_state(ObClient *self)
+{
+ GList *it;
+
+ ob_debug_type(OB_DEBUG_SM,
+ "Restore session for client %s", self->title);
+
+ if (!(it = session_state_find(self))) {
+ ob_debug_type(OB_DEBUG_SM,
+ "Session data not found for client %s", self->title);
+ return;
+ }
+
+ self->session = it->data;
+
+ ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s",
+ self->title);
+
+ RECT_SET_POINT(self->area, self->session->x, self->session->y);
+ self->positioned = USPosition;
+ self->sized = USSize;
+ if (self->session->w > 0)
+ self->area.width = self->session->w;
+ if (self->session->h > 0)
+ self->area.height = self->session->h;
+ XResizeWindow(obt_display, self->window,
+ self->area.width, self->area.height);
+
+ self->desktop = (self->session->desktop == DESKTOP_ALL ?
+ self->session->desktop :
+ MIN(screen_num_desktops - 1, self->session->desktop));
+ OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
+
+ self->shaded = self->session->shaded;
+ self->iconic = self->session->iconic;
+ self->skip_pager = self->session->skip_pager;
+ self->skip_taskbar = self->session->skip_taskbar;
+ self->fullscreen = self->session->fullscreen;
+ self->above = self->session->above;
+ self->below = self->session->below;
+ self->max_horz = self->session->max_horz;
+ self->max_vert = self->session->max_vert;
+ self->undecorated = self->session->undecorated;
+}
+
+static gboolean client_restore_session_stacking(ObClient *self)
+{
+ GList *it, *mypos;
+
+ if (!self->session) return FALSE;
+
+ mypos = g_list_find(session_saved_state, self->session);
+ if (!mypos) return FALSE;
+
+ /* start above me and look for the first client */
+ for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
+ GList *cit;
+
+ for (cit = client_list; cit; cit = g_list_next(cit)) {
+ ObClient *c = cit->data;
+ /* found a client that was in the session, so go below it */
+ if (c->session == it->data) {
+ stacking_below(CLIENT_AS_WINDOW(self),
+ CLIENT_AS_WINDOW(cit->data));
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+void client_move_onscreen(ObClient *self, gboolean rude)
+{
+ gint x = self->area.x;
+ gint y = self->area.y;
+ if (client_find_onscreen(self, &x, &y,
+ self->area.width,
+ self->area.height, rude)) {
+ client_move(self, x, y);
+ }
+}
+
+gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
+ gboolean rude)
+{
+ gint ox = *x, oy = *y;
+ gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
+ gint fw, fh;
+ Rect desired;
+ guint i;
+ gboolean found_mon;
+
+ RECT_SET(desired, *x, *y, w, h);
+ frame_rect_to_frame(self->frame, &desired);
+
+ /* get where the frame would be */
+ frame_client_gravity(self->frame, x, y);
+
+ /* get the requested size of the window with decorations */
+ fw = self->frame->size.left + w + self->frame->size.right;
+ fh = self->frame->size.top + h + self->frame->size.bottom;
+
+ /* If rudeness wasn't requested, then still be rude in a given direction
+ if the client is not moving, only resizing in that direction */
+ if (!rude) {
+ Point oldtl, oldtr, oldbl, oldbr;
+ Point newtl, newtr, newbl, newbr;
+ gboolean stationary_l, stationary_r, stationary_t, stationary_b;
+
+ POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
+ POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
+ self->frame->area.y + self->frame->area.height - 1);
+ POINT_SET(oldtr, oldbr.x, oldtl.y);
+ POINT_SET(oldbl, oldtl.x, oldbr.y);
+
+ POINT_SET(newtl, *x, *y);
+ POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
+ POINT_SET(newtr, newbr.x, newtl.y);
+ POINT_SET(newbl, newtl.x, newbr.y);
+
+ /* is it moving or just resizing from some corner? */
+ stationary_l = oldtl.x == newtl.x;
+ stationary_r = oldtr.x == newtr.x;
+ stationary_t = oldtl.y == newtl.y;
+ stationary_b = oldbl.y == newbl.y;
+
+ /* if left edge is growing and didnt move right edge */
+ if (stationary_r && newtl.x < oldtl.x)
+ rudel = TRUE;
+ /* if right edge is growing and didnt move left edge */
+ if (stationary_l && newtr.x > oldtr.x)
+ ruder = TRUE;
+ /* if top edge is growing and didnt move bottom edge */
+ if (stationary_b && newtl.y < oldtl.y)
+ rudet = TRUE;
+ /* if bottom edge is growing and didnt move top edge */
+ if (stationary_t && newbl.y > oldbl.y)
+ rudeb = TRUE;
+ }
+
+ /* we iterate through every monitor that the window is at least partially
+ on, to make sure it is obeying the rules on them all
+
+ if the window does not appear on any monitors, then use the first one
+ */
+ found_mon = FALSE;
+ for (i = 0; i < screen_num_monitors; ++i) {
+ Rect *a;
+
+ if (!screen_physical_area_monitor_contains(i, &desired)) {
+ if (i < screen_num_monitors - 1 || found_mon)
+ continue;
+
+ /* the window is not inside any monitor! so just use the first
+ one */
+ a = screen_area(self->desktop, 0, NULL);
+ } else {
+ found_mon = TRUE;
+ a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
+ }
+
+ /* This makes sure windows aren't entirely outside of the screen so you
+ can't see them at all.
+ It makes sure 10% of the window is on the screen at least. And don't
+ let it move itself off the top of the screen, which would hide the
+ titlebar on you. (The user can still do this if they want too, it's
+ only limiting the application.
+ */
+ if (client_normal(self)) {
+ if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
+ *x = a->x + a->width - fw/10;
+ if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
+ *y = a->y + a->height - fh/10;
+ if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
+ *x = a->x - fw*9/10;
+ if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
+ *y = a->y - fh*9/10;
+ }
+
+ /* This here doesn't let windows even a pixel outside the
+ struts/screen. When called from client_manage, programs placing
+ themselves are forced completely onscreen, while things like
+ xterm -geometry resolution-width/2 will work fine. Trying to
+ place it completely offscreen will be handled in the above code.
+ Sorry for this confused comment, i am tired. */
+ if (rudel && !self->strut.left && *x < a->x) *x = a->x;
+ if (ruder && !self->strut.right && *x + fw > a->x + a->width)
+ *x = a->x + MAX(0, a->width - fw);
+
+ if (rudet && !self->strut.top && *y < a->y) *y = a->y;
+ if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
+ *y = a->y + MAX(0, a->height - fh);
+
+ g_slice_free(Rect, a);
+ }
+
+ /* get where the client should be */
+ frame_frame_gravity(self->frame, x, y);
+
+ return ox != *x || oy != *y;
+}
+
+static void client_get_all(ObClient *self, gboolean real)
+{
+ /* this is needed for the frame to set itself up */
+ client_get_area(self);
+
+ /* these things can change the decor and functions of the window */
+
+ client_get_mwm_hints(self);
+ /* this can change the mwmhints for special cases */
+ client_get_type_and_transientness(self);
+ client_update_normal_hints(self);
+
+ /* set up the maximum possible decor/functions */
+ client_setup_default_decor_and_functions(self);
+
+ client_get_state(self);
+
+ /* get the session related properties, these can change decorations
+ from per-app settings */
+ client_get_session_ids(self);
+
+ /* now we got everything that can affect the decorations */
+ 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);
+
+ client_update_protocols(self);
+
+ client_update_wmhints(self);
+ /* this may have already been called from client_update_wmhints */
+ if (!self->parents && !self->transient_for_group)
+ client_update_transient_for(self);
+
+ client_get_startup_id(self);
+ client_get_desktop(self);/* uses transient data/group/startup id if a
+ desktop is not specified */
+ client_get_shaped(self);
+
+ {
+ /* a couple type-based defaults for new windows */
+
+ /* this makes sure that these windows appear on all desktops */
+ if (self->type == OB_CLIENT_TYPE_DESKTOP)
+ self->desktop = DESKTOP_ALL;
+ }
+
+#ifdef SYNC
+ client_update_sync_request_counter(self);
+#endif
+
+ client_get_colormap(self);
+ client_update_strut(self);
+ client_update_icons(self);
+ client_update_icon_geometry(self);
+}
+
+static void client_get_startup_id(ObClient *self)
+{
+ if (!(OBT_PROP_GETS_UTF8(self->window, NET_STARTUP_ID, &self->startup_id)))
+ if (self->group)
+ OBT_PROP_GETS_UTF8(self->group->leader, NET_STARTUP_ID,
+ &self->startup_id);
+}
+
+static void client_get_area(ObClient *self)
+{
+ XWindowAttributes wattrib;
+ Status ret;
+
+ ret = XGetWindowAttributes(obt_display, self->window, &wattrib);
+ g_assert(ret != BadWindow);
+
+ RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
+ POINT_SET(self->root_pos, wattrib.x, wattrib.y);
+ self->border_width = wattrib.border_width;
+
+ ob_debug("client area: %d %d %d %d bw %d", wattrib.x, wattrib.y,
+ wattrib.width, wattrib.height, wattrib.border_width);
+}
+
+static void client_get_desktop(ObClient *self)
+{
+ guint32 d = screen_num_desktops; /* an always-invalid value */
+
+ if (OBT_PROP_GET32(self->window, NET_WM_DESKTOP, CARDINAL, &d)) {
+ if (d >= screen_num_desktops && d != DESKTOP_ALL)
+ self->desktop = screen_num_desktops - 1;
+ else
+ self->desktop = d;
+ ob_debug("client requested desktop 0x%x", self->desktop);
+ } else {
+ GSList *it;
+ gboolean first = TRUE;
+ guint all = screen_num_desktops; /* not a valid value */
+
+ /* if they are all on one desktop, then open it on the
+ same desktop */
+ for (it = self->parents; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+
+ if (c->desktop == DESKTOP_ALL) continue;
+
+ if (first) {
+ all = c->desktop;
+ first = FALSE;
+ }
+ else if (all != c->desktop)
+ all = screen_num_desktops; /* make it invalid */
+ }
+ if (all != screen_num_desktops) {
+ self->desktop = all;
+
+ ob_debug("client desktop set from parents: 0x%x",
+ self->desktop);
+ }
+ /* try get from the startup-notification protocol */
+ else if (sn_get_desktop(self->startup_id, &self->desktop)) {
+ if (self->desktop >= screen_num_desktops &&
+ self->desktop != DESKTOP_ALL)
+ self->desktop = screen_num_desktops - 1;
+ ob_debug("client desktop set from startup-notification: 0x%x",
+ self->desktop);
+ }
+ /* defaults to the current desktop */
+ else {
+ self->desktop = screen_desktop;
+ ob_debug("client desktop set to the current desktop: %d",
+ self->desktop);
+ }
+ }
+}
+
+static void client_get_state(ObClient *self)
+{
+ guint32 *state;
+ guint num;
+
+ if (OBT_PROP_GETA32(self->window, NET_WM_STATE, ATOM, &state, &num)) {
+ gulong i;
+ for (i = 0; i < num; ++i) {
+ if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
+ self->modal = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
+ self->shaded = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
+ self->iconic = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
+ self->skip_taskbar = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
+ self->skip_pager = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
+ self->fullscreen = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
+ self->max_vert = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
+ self->max_horz = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
+ self->above = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
+ self->below = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
+ self->demands_attention = TRUE;
+ else if (state[i] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
+ self->undecorated = TRUE;
+ }
+
+ g_free(state);
+ }
+}
+
+static void client_get_shaped(ObClient *self)
+{
+ self->shaped = FALSE;
+#ifdef SHAPE
+ if (obt_display_extension_shape) {
+ gint foo;
+ guint ufoo;
+ gint s;
+
+ XShapeSelectInput(obt_display, self->window, ShapeNotifyMask);
+
+ XShapeQueryExtents(obt_display, self->window, &s, &foo,
+ &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
+ &ufoo);
+ self->shaped = !!s;
+ }
+#endif
+}
+
+void client_update_transient_for(ObClient *self)
+{
+ Window t = None;
+ ObClient *target = NULL;
+ gboolean trangroup = FALSE;
+
+ if (XGetTransientForHint(obt_display, self->window, &t)) {
+ if (t != self->window) { /* can't be transient to itself! */
+ ObWindow *tw = window_find(t);
+ /* if this happens then we need to check for it */
+ g_assert(tw != CLIENT_AS_WINDOW(self));
+ if (tw && WINDOW_IS_CLIENT(tw)) {
+ /* watch out for windows with a parent that is something
+ different, like a dockapp for example */
+ target = WINDOW_AS_CLIENT(tw);
+ }
+ }
+
+ /* Setting the transient_for to Root is actually illegal, however
+ applications from time have done this to specify transient for
+ their group */
+ if (!target && self->group && t == obt_root(ob_screen))
+ trangroup = TRUE;
+ } else if (self->group && self->transient)
+ trangroup = TRUE;
+
+ client_update_transient_tree(self, self->group, self->group,
+ self->transient_for_group, trangroup,
+ client_direct_parent(self), target);
+ self->transient_for_group = trangroup;
+
+}
+
+static void client_update_transient_tree(ObClient *self,
+ ObGroup *oldgroup, ObGroup *newgroup,
+ gboolean oldgtran, gboolean newgtran,
+ ObClient* oldparent,
+ ObClient *newparent)
+{
+ GSList *it, *next;
+ ObClient *c;
+
+ g_assert(!oldgtran || oldgroup);
+ g_assert(!newgtran || newgroup);
+ g_assert((!oldgtran && !oldparent) ||
+ (oldgtran && !oldparent) ||
+ (!oldgtran && oldparent));
+ g_assert((!newgtran && !newparent) ||
+ (newgtran && !newparent) ||
+ (!newgtran && newparent));
+
+ /* * *
+ Group transient windows are not allowed to have other group
+ transient windows as their children.
+ * * */
+
+ /* No change has occured */
+ if (oldgroup == newgroup &&
+ oldgtran == newgtran &&
+ oldparent == newparent) return;
+
+ /** Remove the client from the transient tree **/
+
+ for (it = self->transients; it; it = next) {
+ next = g_slist_next(it);
+ c = it->data;
+ self->transients = g_slist_delete_link(self->transients, it);
+ c->parents = g_slist_remove(c->parents, self);
+ }
+ for (it = self->parents; it; it = next) {
+ next = g_slist_next(it);
+ c = it->data;
+ self->parents = g_slist_delete_link(self->parents, it);
+ c->transients = g_slist_remove(c->transients, self);
+ }
+
+ /** Re-add the client to the transient tree **/
+
+ /* If we're transient for a group then we need to add ourselves to all our
+ parents */
+ if (newgtran) {
+ for (it = newgroup->members; it; it = g_slist_next(it)) {
+ c = it->data;
+ if (c != self &&
+ !client_search_top_direct_parent(c)->transient_for_group &&
+ client_normal(c))
+ {
+ c->transients = g_slist_prepend(c->transients, self);
+ self->parents = g_slist_prepend(self->parents, c);
+ }
+ }
+ }
+
+ /* If we are now transient for a single window we need to add ourselves to
+ its children
+
+ WARNING: Cyclical transient-ness is possible if two windows are
+ transient for eachother.
+ */
+ else if (newparent &&
+ /* don't make ourself its child if it is already our child */
+ !client_is_direct_child(self, newparent) &&
+ client_normal(newparent))
+ {
+ newparent->transients = g_slist_prepend(newparent->transients, self);
+ self->parents = g_slist_prepend(self->parents, newparent);
+ }
+
+ /* Add any group transient windows to our children. But if we're transient
+ for the group, then other group transients are not our children.
+
+ WARNING: Cyclical transient-ness is possible. For e.g. if:
+ A is transient for the group
+ B is transient for A
+ C is transient for B
+ A can't be transient for C or we have a cycle
+ */
+ if (!newgtran && newgroup &&
+ (!newparent ||
+ !client_search_top_direct_parent(newparent)->transient_for_group) &&
+ client_normal(self))
+ {
+ for (it = newgroup->members; it; it = g_slist_next(it)) {
+ c = it->data;
+ if (c != self && c->transient_for_group &&
+ /* Don't make it our child if it is already our parent */
+ !client_is_direct_child(c, self))
+ {
+ self->transients = g_slist_prepend(self->transients, c);
+ c->parents = g_slist_prepend(c->parents, self);
+ }
+ }
+ }
+
+ /** If we change our group transient-ness, our children change their
+ effective group transient-ness, which affects how they relate to other
+ group windows **/
+
+ for (it = self->transients; it; it = g_slist_next(it)) {
+ c = it->data;
+ if (!c->transient_for_group)
+ client_update_transient_tree(c, c->group, c->group,
+ c->transient_for_group,
+ c->transient_for_group,
+ client_direct_parent(c),
+ client_direct_parent(c));
+ }
+}
+
+void client_get_mwm_hints(ObClient *self)
+{
+ guint num;
+ guint32 *hints;
+
+ self->mwmhints.flags = 0; /* default to none */
+
+ if (OBT_PROP_GETA32(self->window, MOTIF_WM_HINTS, MOTIF_WM_HINTS,
+ &hints, &num)) {
+ if (num >= OB_MWM_ELEMENTS) {
+ self->mwmhints.flags = hints[0];
+ self->mwmhints.functions = hints[1];
+ self->mwmhints.decorations = hints[2];
+ }
+ g_free(hints);
+ }
+}
+
+void client_get_type_and_transientness(ObClient *self)
+{
+ guint num, i;
+ guint32 *val;
+ Window t;
+
+ self->type = -1;
+ self->transient = FALSE;
+
+ if (OBT_PROP_GETA32(self->window, NET_WM_WINDOW_TYPE, ATOM, &val, &num)) {
+ /* use the first value that we know about in the array */
+ for (i = 0; i < num; ++i) {
+ if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
+ self->type = OB_CLIENT_TYPE_DESKTOP;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK))
+ self->type = OB_CLIENT_TYPE_DOCK;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR))
+ self->type = OB_CLIENT_TYPE_TOOLBAR;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
+ self->type = OB_CLIENT_TYPE_MENU;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY))
+ self->type = OB_CLIENT_TYPE_UTILITY;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH))
+ self->type = OB_CLIENT_TYPE_SPLASH;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG))
+ self->type = OB_CLIENT_TYPE_DIALOG;
+ else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL))
+ self->type = OB_CLIENT_TYPE_NORMAL;
+ else if (val[i] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE))
+ {
+ /* prevent this window from getting any decor or
+ functionality */
+ self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
+ OB_MWM_FLAG_DECORATIONS);
+ self->mwmhints.decorations = 0;
+ self->mwmhints.functions = 0;
+ }
+ if (self->type != (ObClientType) -1)
+ break; /* grab the first legit type */
+ }
+ g_free(val);
+ }
+
+ if (XGetTransientForHint(obt_display, self->window, &t))
+ self->transient = TRUE;
+
+ if (self->type == (ObClientType) -1) {
+ /*the window type hint was not set, which means we either classify
+ ourself as a normal window or a dialog, depending on if we are a
+ transient. */
+ if (self->transient)
+ self->type = OB_CLIENT_TYPE_DIALOG;
+ else
+ self->type = OB_CLIENT_TYPE_NORMAL;
+ }
+
+ /* then, based on our type, we can update our transientness.. */
+ if (self->type == OB_CLIENT_TYPE_DIALOG ||
+ self->type == OB_CLIENT_TYPE_TOOLBAR ||
+ self->type == OB_CLIENT_TYPE_MENU ||
+ self->type == OB_CLIENT_TYPE_UTILITY)
+ {
+ self->transient = TRUE;
+ }
+}
+
+void client_update_protocols(ObClient *self)
+{
+ guint32 *proto;
+ guint num_ret, i;
+
+ self->focus_notify = FALSE;
+ self->delete_window = FALSE;
+
+ if (OBT_PROP_GETA32(self->window, WM_PROTOCOLS, ATOM, &proto, &num_ret)) {
+ for (i = 0; i < num_ret; ++i) {
+ if (proto[i] == OBT_PROP_ATOM(WM_DELETE_WINDOW))
+ /* this means we can request the window to close */
+ self->delete_window = TRUE;
+ else if (proto[i] == OBT_PROP_ATOM(WM_TAKE_FOCUS))
+ /* if this protocol is requested, then the window will be
+ notified whenever we want it to receive focus */
+ self->focus_notify = TRUE;
+ else if (proto[i] == OBT_PROP_ATOM(NET_WM_PING))
+ /* if this protocol is requested, then the window will allow
+ pings to determine if it is still alive */
+ self->ping = TRUE;
+#ifdef SYNC
+ else if (proto[i] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST))
+ /* if this protocol is requested, then resizing the
+ window will be synchronized between the frame and the
+ client */
+ self->sync_request = TRUE;
+#endif
+ }
+ g_free(proto);
+ }
+}
+
+#ifdef SYNC
+void client_update_sync_request_counter(ObClient *self)
+{
+ guint32 i;
+
+ if (OBT_PROP_GET32(self->window, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i))
+ {
+ XSyncValue val;
+
+ self->sync_counter = i;
+
+ /* this must be set when managing a new window according to EWMH */
+ XSyncIntToValue(&val, 0);
+ XSyncSetCounter(obt_display, self->sync_counter, val);
+ } else
+ self->sync_counter = None;
+}
+#endif
+
+static void client_get_colormap(ObClient *self)
+{
+ XWindowAttributes wa;
+
+ if (XGetWindowAttributes(obt_display, self->window, &wa))
+ client_update_colormap(self, wa.colormap);
+}
+
+void client_update_colormap(ObClient *self, Colormap colormap)
+{
+ if (colormap == self->colormap) return;
+
+ ob_debug("Setting client %s colormap: 0x%x", self->title, colormap);
+
+ if (client_focused(self)) {
+ screen_install_colormap(self, FALSE); /* uninstall old one */
+ self->colormap = colormap;
+ screen_install_colormap(self, TRUE); /* install new one */
+ } else
+ self->colormap = colormap;
+}
+
+void client_update_opacity(ObClient *self)
+{
+ guint32 o;
+
+ if (OBT_PROP_GET32(self->window, NET_WM_WINDOW_OPACITY, CARDINAL, &o))
+ OBT_PROP_SET32(self->frame->window, NET_WM_WINDOW_OPACITY, CARDINAL, o);
+ else
+ OBT_PROP_ERASE(self->frame->window, NET_WM_WINDOW_OPACITY);
+}
+
+void client_update_normal_hints(ObClient *self)
+{
+ XSizeHints size;
+ glong ret;
+
+ /* defaults */
+ self->min_ratio = 0.0f;
+ self->max_ratio = 0.0f;
+ SIZE_SET(self->size_inc, 1, 1);
+ SIZE_SET(self->base_size, -1, -1);
+ SIZE_SET(self->min_size, 0, 0);
+ SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
+
+ /* get the hints from the window */
+ if (XGetWMNormalHints(obt_display, self->window, &size, &ret)) {
+ /* normal windows can't request placement! har har
+ if (!client_normal(self))
+ */
+ self->positioned = (size.flags & (PPosition|USPosition));
+ self->sized = (size.flags & (PSize|USSize));
+
+ if (size.flags & PWinGravity)
+ self->gravity = size.win_gravity;
+
+ if (size.flags & PAspect) {
+ if (size.min_aspect.y)
+ self->min_ratio =
+ (gfloat) size.min_aspect.x / size.min_aspect.y;
+ if (size.max_aspect.y)
+ self->max_ratio =
+ (gfloat) size.max_aspect.x / size.max_aspect.y;
+ }
+
+ if (size.flags & PMinSize)
+ SIZE_SET(self->min_size, size.min_width, size.min_height);
+
+ if (size.flags & PMaxSize)
+ SIZE_SET(self->max_size, size.max_width, size.max_height);
+
+ if (size.flags & PBaseSize)
+ SIZE_SET(self->base_size, size.base_width, size.base_height);
+
+ if (size.flags & PResizeInc && size.width_inc && size.height_inc)
+ SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
+
+ ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
+ self->min_size.width, self->min_size.height,
+ self->max_size.width, self->max_size.height);
+ ob_debug("size inc (%d %d) base size (%d %d)",
+ self->size_inc.width, self->size_inc.height,
+ self->base_size.width, self->base_size.height);
+ }
+ else
+ ob_debug("Normal hints: not set");
+}
+
+static void client_setup_default_decor_and_functions(ObClient *self)
+{
+ /* start with everything (cept fullscreen) */
+ self->decorations =
+ (OB_FRAME_DECOR_TITLEBAR |
+ OB_FRAME_DECOR_HANDLE |
+ OB_FRAME_DECOR_GRIPS |
+ OB_FRAME_DECOR_BORDER |
+ OB_FRAME_DECOR_ICON |
+ OB_FRAME_DECOR_ALLDESKTOPS |
+ OB_FRAME_DECOR_ICONIFY |
+ OB_FRAME_DECOR_MAXIMIZE |
+ OB_FRAME_DECOR_SHADE |
+ OB_FRAME_DECOR_CLOSE);
+ self->functions =
+ (OB_CLIENT_FUNC_RESIZE |
+ OB_CLIENT_FUNC_MOVE |
+ OB_CLIENT_FUNC_ICONIFY |
+ OB_CLIENT_FUNC_MAXIMIZE |
+ OB_CLIENT_FUNC_SHADE |
+ OB_CLIENT_FUNC_CLOSE |
+ OB_CLIENT_FUNC_BELOW |
+ OB_CLIENT_FUNC_ABOVE |
+ OB_CLIENT_FUNC_UNDECORATE);
+
+ if (!(self->min_size.width < self->max_size.width ||
+ self->min_size.height < self->max_size.height))
+ self->functions &= ~OB_CLIENT_FUNC_RESIZE;
+
+ switch (self->type) {
+ case OB_CLIENT_TYPE_NORMAL:
+ /* normal windows retain all of the possible decorations and
+ functionality, and can be fullscreen */
+ self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
+ break;
+
+ case OB_CLIENT_TYPE_DIALOG:
+ /* sometimes apps make dialog windows fullscreen for some reason (for
+ e.g. kpdf does this..) */
+ self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
+ break;
+
+ case OB_CLIENT_TYPE_UTILITY:
+ /* these windows don't have anything added or removed by default */
+ break;
+
+ case OB_CLIENT_TYPE_MENU:
+ case OB_CLIENT_TYPE_TOOLBAR:
+ /* these windows can't iconify or maximize */
+ self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
+ OB_FRAME_DECOR_MAXIMIZE);
+ self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
+ OB_CLIENT_FUNC_MAXIMIZE);
+ break;
+
+ case OB_CLIENT_TYPE_SPLASH:
+ /* these don't get get any decorations, and the only thing you can
+ do with them is move them */
+ self->decorations = 0;
+ self->functions = OB_CLIENT_FUNC_MOVE;
+ break;
+
+ case OB_CLIENT_TYPE_DESKTOP:
+ /* these windows are not manipulated by the window manager */
+ self->decorations = 0;
+ self->functions = 0;
+ break;
+
+ case OB_CLIENT_TYPE_DOCK:
+ /* these windows are not manipulated by the window manager, but they
+ can set below layer which has a special meaning */
+ self->decorations = 0;
+ self->functions = OB_CLIENT_FUNC_BELOW;
+ break;
+ }
+
+ /* If the client has no decor from its type (which never changes) then
+ don't allow the user to "undecorate" the window. Otherwise, allow them
+ to, even if there are motif hints removing the decor, because those
+ may change these days (e.g. chromium) */
+ if (self->decorations == 0)
+ self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
+
+ /* Mwm Hints are applied subtractively to what has already been chosen for
+ decor and functionality */
+ if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
+ if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
+ if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
+ (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
+ {
+ /* if the mwm hints request no handle or title, then all
+ decorations are disabled, but keep the border if that's
+ specified */
+ if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
+ self->decorations = OB_FRAME_DECOR_BORDER;
+ else
+ self->decorations = 0;
+ }
+ }
+ }
+
+ if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
+ if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
+ if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
+ self->functions &= ~OB_CLIENT_FUNC_RESIZE;
+ if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
+ self->functions &= ~OB_CLIENT_FUNC_MOVE;
+ /* dont let mwm hints kill any buttons
+ if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
+ self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
+ if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
+ self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
+ */
+ /* dont let mwm hints kill the close button
+ if (! (self->mwmhints.functions & MwmFunc_Close))
+ self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
+ }
+ }
+
+ if (!(self->functions & OB_CLIENT_FUNC_SHADE))
+ self->decorations &= ~OB_FRAME_DECOR_SHADE;
+ if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
+ self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
+ if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
+ self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
+
+ /* can't maximize without moving/resizing */
+ if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
+ (self->functions & OB_CLIENT_FUNC_MOVE) &&
+ (self->functions & OB_CLIENT_FUNC_RESIZE))) {
+ self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
+ self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
+ }
+}
+
+/*! Set up decor for a client based on its undecorated state. */
+static void client_setup_decor_undecorated(ObClient *self)
+{
+ /* If the user requested no decorations, then remove all the decorations,
+ except the border. But don't add a border if there wasn't one. */
+ if (self->undecorated)
+ self->decorations &= (config_theme_keepborder ?
+ OB_FRAME_DECOR_BORDER : 0);
+}
+
+void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
+{
+ client_setup_default_decor_and_functions(self);
+
+ client_setup_decor_undecorated(self);
+
+ if (self->max_horz && self->max_vert) {
+ /* once upon a time you couldn't resize maximized windows, that is not
+ the case any more though !
+
+ but do kill the handle on fully maxed windows */
+ self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
+ }
+
+ /* if we don't have a titlebar, then we cannot shade! */
+ if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
+ self->functions &= ~OB_CLIENT_FUNC_SHADE;
+
+ /* now we need to check against rules for the client's current state */
+ if (self->fullscreen) {
+ self->functions &= (OB_CLIENT_FUNC_CLOSE |
+ OB_CLIENT_FUNC_FULLSCREEN |
+ OB_CLIENT_FUNC_ICONIFY);
+ self->decorations = 0;
+ }
+
+ client_change_allowed_actions(self);
+
+ if (reconfig)
+ /* reconfigure to make sure decorations are updated */
+ client_reconfigure(self, FALSE);
+}
+
+static void client_change_allowed_actions(ObClient *self)
+{
+ gulong actions[12];
+ gint num = 0;
+
+ /* desktop windows are kept on all desktops */
+ if (self->type != OB_CLIENT_TYPE_DESKTOP)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
+
+ if (self->functions & OB_CLIENT_FUNC_SHADE)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
+ if (self->functions & OB_CLIENT_FUNC_CLOSE)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
+ if (self->functions & OB_CLIENT_FUNC_MOVE)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
+ if (self->functions & OB_CLIENT_FUNC_ICONIFY)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
+ if (self->functions & OB_CLIENT_FUNC_RESIZE)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
+ if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
+ if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
+ }
+ if (self->functions & OB_CLIENT_FUNC_ABOVE)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
+ if (self->functions & OB_CLIENT_FUNC_BELOW)
+ actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
+ if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
+ actions[num++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
+
+ OBT_PROP_SETA32(self->window, NET_WM_ALLOWED_ACTIONS, ATOM, actions, num);
+
+ /* make sure the window isn't breaking any rules now
+
+ don't check ICONIFY here. just cuz a window can't iconify doesnt mean
+ it can't be iconified with its parent
+ */
+
+ if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
+ if (self->frame) client_shade(self, FALSE);
+ else self->shaded = FALSE;
+ }
+ if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
+ if (self->frame) client_fullscreen(self, FALSE);
+ else self->fullscreen = FALSE;
+ }
+ if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
+ self->max_vert)) {
+ if (self->frame) client_maximize(self, FALSE, 0);
+ else self->max_vert = self->max_horz = FALSE;
+ }
+}
+
+void client_update_wmhints(ObClient *self)
+{
+ XWMHints *hints;
+
+ /* assume a window takes input if it doesn't specify */
+ self->can_focus = TRUE;
+
+ if ((hints = XGetWMHints(obt_display, self->window)) != NULL) {
+ gboolean ur;
+
+ if (hints->flags & InputHint)
+ self->can_focus = hints->input;
+
+ /* only do this when first managing the window *AND* when we aren't
+ starting up! */
+ if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
+ if (hints->flags & StateHint)
+ self->iconic = hints->initial_state == IconicState;
+
+ ur = self->urgent;
+ self->urgent = (hints->flags & XUrgencyHint);
+ if (self->urgent && !ur)
+ client_hilite(self, TRUE);
+ else if (!self->urgent && ur && self->demands_attention)
+ client_hilite(self, FALSE);
+
+ if (!(hints->flags & WindowGroupHint))
+ hints->window_group = None;
+
+ /* did the group state change? */
+ if (hints->window_group !=
+ (self->group ? self->group->leader : None))
+ {
+ ObGroup *oldgroup = self->group;
+
+ /* remove from the old group if there was one */
+ if (self->group) {
+ group_remove(self->group, self);
+ self->group = NULL;
+ }
+
+ /* add ourself to the group if we have one */
+ if (hints->window_group != None) {
+ self->group = group_add(hints->window_group, self);
+ }
+
+ /* Put ourselves into the new group's transient tree, and remove
+ ourselves from the old group's */
+ client_update_transient_tree(self, oldgroup, self->group,
+ self->transient_for_group,
+ self->transient_for_group,
+ client_direct_parent(self),
+ client_direct_parent(self));
+
+ /* Lastly, being in a group, or not, can change if the window is
+ transient for anything.
+
+ The logic for this is:
+ self->transient = TRUE always if the window wants to be
+ transient for something, even if transient_for was NULL because
+ it wasn't in a group before.
+
+ If parents was NULL and oldgroup was NULL we can assume
+ that when we add the new group, it will become transient for
+ something.
+
+ If transient_for_group is TRUE, then it must have already
+ had a group. If it is getting a new group, the above call to
+ client_update_transient_tree has already taken care of
+ everything ! If it is losing all group status then it will
+ no longer be transient for anything and that needs to be
+ updated.
+ */
+ if (self->transient &&
+ ((self->parents == NULL && oldgroup == NULL) ||
+ (self->transient_for_group && !self->group)))
+ client_update_transient_for(self);
+ }
+
+ /* the WM_HINTS can contain an icon */
+ if (hints->flags & IconPixmapHint)
+ client_update_icons(self);
+
+ XFree(hints);
+ }
+
+ focus_cycle_addremove(self, TRUE);
+}
+
+void client_update_title(ObClient *self)
+{
+ gchar *data = NULL;
+ gchar *visible = NULL;
+
+ g_free(self->title);
+ g_free(self->original_title);
+
+ /* try netwm */
+ if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_NAME, &data)) {
+ /* try old x stuff */
+ if (!OBT_PROP_GETS(self->window, WM_NAME, &data)) {
+ if (self->transient) {
+ /*
+ GNOME alert windows are not given titles:
+ http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
+ */
+ data = g_strdup("");
+ } else
+ data = g_strdup(_("Unnamed Window"));
+ }
+ }
+ self->original_title = g_strdup(data);
+
+ if (self->client_machine) {
+ visible = g_strdup_printf("%s (%s)", data, self->client_machine);
+ g_free(data);
+ } else
+ visible = data;
+
+ if (self->not_responding) {
+ data = visible;
+ if (self->kill_level > 0)
+ visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
+ else
+ visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
+ g_free(data);
+ }
+
+ OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, visible);
+ self->title = visible;
+
+ if (self->frame)
+ frame_adjust_title(self->frame);
+
+ /* update the icon title */
+ data = NULL;
+ g_free(self->icon_title);
+
+ /* try netwm */
+ if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_ICON_NAME, &data))
+ /* try old x stuff */
+ if (!OBT_PROP_GETS(self->window, WM_ICON_NAME, &data))
+ data = g_strdup(self->title);
+
+ if (self->client_machine) {
+ visible = g_strdup_printf("%s (%s)", data, self->client_machine);
+ g_free(data);
+ } else
+ visible = data;
+
+ if (self->not_responding) {
+ data = visible;
+ if (self->kill_level > 0)
+ visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
+ else
+ visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
+ g_free(data);
+ }
+
+ OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, visible);
+ self->icon_title = visible;
+}
+
+void client_update_strut(ObClient *self)
+{
+ guint num;
+ guint32 *data;
+ gboolean got = FALSE;
+ StrutPartial strut;
+
+ if (OBT_PROP_GETA32(self->window, NET_WM_STRUT_PARTIAL, CARDINAL,
+ &data, &num))
+ {
+ if (num == 12) {
+ got = TRUE;
+ STRUT_PARTIAL_SET(strut,
+ data[0], data[2], data[1], data[3],
+ data[4], data[5], data[8], data[9],
+ data[6], data[7], data[10], data[11]);
+ }
+ g_free(data);
+ }
+
+ if (!got &&
+ OBT_PROP_GETA32(self->window, NET_WM_STRUT, CARDINAL, &data, &num)) {
+ if (num == 4) {
+ const Rect *a;
+
+ got = TRUE;
+
+ /* use the screen's width/height */
+ a = screen_physical_area_all_monitors();
+
+ STRUT_PARTIAL_SET(strut,
+ data[0], data[2], data[1], data[3],
+ a->y, a->y + a->height - 1,
+ a->x, a->x + a->width - 1,
+ a->y, a->y + a->height - 1,
+ a->x, a->x + a->width - 1);
+ }
+ g_free(data);
+ }
+
+ if (!got)
+ STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+
+ if (!PARTIAL_STRUT_EQUAL(strut, self->strut)) {
+ self->strut = strut;
+
+ /* updating here is pointless while we're being mapped cuz we're not in
+ the client list yet */
+ if (self->frame)
+ screen_update_areas();
+ }
+}
+
+void client_update_icons(ObClient *self)
+{
+ guint num;
+ guint32 *data;
+ guint w, h, i, j;
+ RrImage *img;
+
+ img = NULL;
+
+ /* grab the server, because we might be setting the window's icon and
+ we don't want them to set it in between and we overwrite their own
+ icon */
+ grab_server(TRUE);
+
+ if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) {
+ /* figure out how many valid icons are in here */
+ i = 0;
+ while (i + 2 < num) { /* +2 is to make sure there is a w and h */
+ w = data[i++];
+ h = data[i++];
+ /* watch for the data being too small for the specified size,
+ or for zero sized icons. */
+ if (i + w*h > num || w == 0 || h == 0) {
+ i += w*h;
+ continue;
+ }
+
+ /* convert it to the right bit order for ObRender */
+ for (j = 0; j < w*h; ++j)
+ data[i+j] =
+ (((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) +
+ (((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset) +
+ (((data[i+j] >> 8) & 0xff) << RrDefaultGreenOffset) +
+ (((data[i+j] >> 0) & 0xff) << RrDefaultBlueOffset);
+
+ /* add it to the image cache as an original */
+ if (!img)
+ img = RrImageNewFromData(ob_rr_icons, &data[i], w, h);
+ else
+ RrImageAddFromData(img, &data[i], w, h);
+
+ i += w*h;
+ }
+
+ g_free(data);
+ }
+
+ /* if we didn't find an image from the NET_WM_ICON stuff, then try the
+ legacy X hints */
+ if (!img) {
+ XWMHints *hints;
+
+ if ((hints = XGetWMHints(obt_display, self->window))) {
+ if (hints->flags & IconPixmapHint) {
+ gboolean xicon;
+ obt_display_ignore_errors(TRUE);
+ xicon = RrPixmapToRGBA(ob_rr_inst,
+ hints->icon_pixmap,
+ (hints->flags & IconMaskHint ?
+ hints->icon_mask : None),
+ (gint*)&w, (gint*)&h, &data);
+ obt_display_ignore_errors(FALSE);
+
+ if (xicon) {
+ if (w > 0 && h > 0) {
+ if (!img)
+ img = RrImageNewFromData(ob_rr_icons, data, w, h);
+ else
+ RrImageAddFromData(img, data, w, h);
+ }
+
+ g_free(data);
+ }
+ }
+ XFree(hints);
+ }
+ }
+
+ /* set the client's icons to be whatever we found */
+ RrImageUnref(self->icon_set);
+ self->icon_set = img;
+
+ /* if the client has no icon at all, then we set a default icon onto it.
+ but, if it has parents, then one of them will have an icon already
+ */
+ if (!self->icon_set && !self->parents) {
+ RrPixel32 *icon = ob_rr_theme->def_win_icon;
+ gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */
+
+ w = ob_rr_theme->def_win_icon_w;
+ h = ob_rr_theme->def_win_icon_h;
+ ldata = g_new(gulong, w*h+2);
+ ldata[0] = w;
+ ldata[1] = h;
+ for (i = 0; i < w*h; ++i)
+ ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
+ (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
+ (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
+ (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
+ OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, ldata, w*h+2);
+ g_free(ldata);
+ } else if (self->frame)
+ /* don't draw the icon empty if we're just setting one now anyways,
+ we'll get the property change any second */
+ frame_adjust_icon(self->frame);
+
+ grab_server(FALSE);
+}
+
+void client_update_icon_geometry(ObClient *self)
+{
+ guint num;
+ guint32 *data;
+
+ RECT_SET(self->icon_geometry, 0, 0, 0, 0);
+
+ if (OBT_PROP_GETA32(self->window, NET_WM_ICON_GEOMETRY, CARDINAL,
+ &data, &num))
+ {
+ if (num == 4)
+ /* don't let them set it with an area < 0 */
+ RECT_SET(self->icon_geometry, data[0], data[1],
+ MAX(data[2],0), MAX(data[3],0));
+ g_free(data);
+ }
+}
+
+static void client_get_session_ids(ObClient *self)
+{
+ guint32 leader;
+ gboolean got;
+ gchar *s;
+ gchar **ss;
+
+ if (!OBT_PROP_GET32(self->window, WM_CLIENT_LEADER, WINDOW, &leader))
+ leader = None;
+
+ /* get the SM_CLIENT_ID */
+ if (leader && leader != self->window)
+ OBT_PROP_GETS_XPCS(leader, SM_CLIENT_ID, &self->sm_client_id);
+ else
+ OBT_PROP_GETS_XPCS(self->window, SM_CLIENT_ID, &self->sm_client_id);
+
+ /* get the WM_CLASS (name and class). make them "" if they are not
+ provided */
+ got = OBT_PROP_GETSS_TYPE(self->window, WM_CLASS, STRING_NO_CC, &ss);
+
+ if (got) {
+ if (ss[0]) {
+ self->name = g_strdup(ss[0]);
+ if (ss[1])
+ self->class = g_strdup(ss[1]);
+ }
+ g_strfreev(ss);
+ }
+
+ if (self->name == NULL) self->name = g_strdup("");
+ if (self->class == NULL) self->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);
+
+ if (got)
+ self->role = s;
+ else
+ self->role = g_strdup("");
+
+ /* get the WM_COMMAND */
+ got = FALSE;
+
+ if (leader)
+ got = OBT_PROP_GETSS(leader, WM_COMMAND, &ss);
+ if (!got)
+ got = OBT_PROP_GETSS(self->window, WM_COMMAND, &ss);
+
+ if (got) {
+ /* merge/mash them all together */
+ gchar *merge = NULL;
+ gint i;
+
+ for (i = 0; ss[i]; ++i) {
+ gchar *tmp = merge;
+ if (merge)
+ merge = g_strconcat(merge, ss[i], NULL);
+ else
+ merge = g_strconcat(ss[i], NULL);
+ g_free(tmp);
+ }
+ g_strfreev(ss);
+
+ self->wm_command = merge;
+ }
+
+ /* get the WM_CLIENT_MACHINE */
+ got = FALSE;
+ if (leader)
+ got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, &s);
+ if (!got)
+ got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, &s);
+
+ if (got) {
+ gchar localhost[128];
+ guint32 pid;
+
+ gethostname(localhost, 127);
+ localhost[127] = '\0';
+ if (strcmp(localhost, s) != 0)
+ self->client_machine = s;
+ else
+ g_free(s);
+
+ /* see if it has the PID set too (the PID requires that the
+ WM_CLIENT_MACHINE be set) */
+ if (OBT_PROP_GET32(self->window, NET_WM_PID, CARDINAL, &pid))
+ self->pid = pid;
+ }
+}
+
+/*! Save the properties used for app matching rules, as seen by Openbox when
+ the window mapped, so that users can still access them later if the app
+ changes them */
+static void client_save_app_rule_values(ObClient *self)
+{
+ const gchar *type;
+
+ 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_TITLE, self->original_title);
+
+ switch (self->type) {
+ case OB_CLIENT_TYPE_NORMAL:
+ type = "normal"; break;
+ case OB_CLIENT_TYPE_DIALOG:
+ type = "dialog"; break;
+ case OB_CLIENT_TYPE_UTILITY:
+ type = "utility"; break;
+ case OB_CLIENT_TYPE_MENU:
+ type = "menu"; break;
+ case OB_CLIENT_TYPE_TOOLBAR:
+ type = "toolbar"; break;
+ case OB_CLIENT_TYPE_SPLASH:
+ type = "splash"; break;
+ case OB_CLIENT_TYPE_DESKTOP:
+ type = "desktop"; break;
+ case OB_CLIENT_TYPE_DOCK:
+ type = "dock"; break;
+ }
+ OBT_PROP_SETS(self->window, OB_APP_TYPE, type);
+}
+
+static void client_change_wm_state(ObClient *self)
+{
+ gulong state[2];
+ glong old;
+
+ old = self->wmstate;
+
+ if (self->shaded || self->iconic ||
+ (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
+ {
+ self->wmstate = IconicState;
+ } else
+ self->wmstate = NormalState;
+
+ if (old != self->wmstate) {
+ OBT_PROP_MSG(ob_screen, self->window, KDE_WM_CHANGE_STATE,
+ self->wmstate, 1, 0, 0, 0);
+
+ state[0] = self->wmstate;
+ state[1] = None;
+ OBT_PROP_SETA32(self->window, WM_STATE, WM_STATE, state, 2);
+ }
+}
+
+static void client_change_state(ObClient *self)
+{
+ gulong netstate[12];
+ guint num;
+
+ num = 0;
+ if (self->modal)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
+ if (self->shaded)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
+ if (self->iconic)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
+ if (self->skip_taskbar)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
+ if (self->skip_pager)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
+ if (self->fullscreen)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
+ if (self->max_vert)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
+ if (self->max_horz)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
+ if (self->above)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
+ if (self->below)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
+ if (self->demands_attention)
+ netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
+ if (self->undecorated)
+ netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
+ OBT_PROP_SETA32(self->window, NET_WM_STATE, ATOM, netstate, num);
+
+ if (self->frame)
+ frame_adjust_state(self->frame);
+}
+
+ObClient *client_search_focus_tree(ObClient *self)
+{
+ GSList *it;
+ ObClient *ret;
+
+ for (it = self->transients; it; it = g_slist_next(it)) {
+ if (client_focused(it->data)) return it->data;
+ if ((ret = client_search_focus_tree(it->data))) return ret;
+ }
+ return NULL;
+}
+
+ObClient *client_search_focus_tree_full(ObClient *self)
+{
+ if (self->parents) {
+ GSList *it;
+
+ for (it = self->parents; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+ if ((c = client_search_focus_tree_full(c))) return c;
+ }
+
+ return NULL;
+ }
+ else {
+ /* this function checks the whole tree, the client_search_focus_tree
+ does not, so we need to check this window */
+ if (client_focused(self))
+ return self;
+ return client_search_focus_tree(self);
+ }
+}
+
+ObClient *client_search_focus_group_full(ObClient *self)
+{
+ GSList *it;
+
+ if (self->group) {
+ for (it = self->group->members; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+
+ if (client_focused(c)) return c;
+ if ((c = client_search_focus_tree(it->data))) return c;
+ }
+ } else
+ if (client_focused(self)) return self;
+ return NULL;
+}
+
+gboolean client_has_parent(ObClient *self)
+{
+ return self->parents != NULL;
+}
+
+gboolean client_has_children(ObClient *self)
+{
+ return self->transients != NULL;
+}
+
+gboolean client_is_oldfullscreen(const ObClient *self,
+ const Rect *area)
+{
+ const Rect *monitor, *allmonitors;
+
+ /* No decorations and fills the monitor = oldskool fullscreen.
+ But not for maximized windows.
+ */
+
+ if (self->decorations || self->max_horz || self->max_vert) return FALSE;
+
+ monitor = screen_physical_area_monitor(screen_find_monitor(area));
+ allmonitors = screen_physical_area_all_monitors();
+
+ return (RECT_EQUAL(*area, *monitor) ||
+ RECT_EQUAL(*area, *allmonitors));
+}
+
+static ObStackingLayer calc_layer(ObClient *self)
+{
+ ObStackingLayer l;
+
+ if (self->type == OB_CLIENT_TYPE_DESKTOP)
+ l = OB_STACKING_LAYER_DESKTOP;
+ else if (self->type == OB_CLIENT_TYPE_DOCK) {
+ if (self->below) l = OB_STACKING_LAYER_NORMAL;
+ else l = OB_STACKING_LAYER_ABOVE;
+ }
+ else if ((self->fullscreen ||
+ client_is_oldfullscreen(self, &self->area)) &&
+ /* you are fullscreen while you or your children are focused.. */
+ (client_focused(self) || client_search_focus_tree(self) ||
+ /* you can be fullscreen if you're on another desktop */
+ (self->desktop != screen_desktop &&
+ self->desktop != DESKTOP_ALL) ||
+ /* and you can also be fullscreen if the focused client is on
+ another monitor, or nothing else is focused */
+ (!focus_client ||
+ client_monitor(focus_client) != client_monitor(self))))
+ l = OB_STACKING_LAYER_FULLSCREEN;
+ else if (self->above) l = OB_STACKING_LAYER_ABOVE;
+ else if (self->below) l = OB_STACKING_LAYER_BELOW;
+ else l = OB_STACKING_LAYER_NORMAL;
+
+ return l;
+}
+
+static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
+ ObStackingLayer min)
+{
+ ObStackingLayer old, own;
+ GSList *it;
+
+ old = self->layer;
+ own = calc_layer(self);
+ self->layer = MAX(own, min);
+
+ if (self->layer != old) {
+ stacking_remove(CLIENT_AS_WINDOW(self));
+ stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
+ }
+
+ /* we've been restacked */
+ self->visited = TRUE;
+
+ for (it = self->transients; it; it = g_slist_next(it))
+ client_calc_layer_recursive(it->data, orig,
+ self->layer);
+}
+
+static void client_calc_layer_internal(ObClient *self)
+{
+ GSList *sit;
+
+ /* transients take on the layer of their parents */
+ sit = client_search_all_top_parents(self);
+
+ for (; sit; sit = g_slist_next(sit))
+ client_calc_layer_recursive(sit->data, self, 0);
+}
+
+void client_calc_layer(ObClient *self)
+{
+ GList *it;
+
+ /* skip over stuff above fullscreen layer */
+ for (it = stacking_list; it; it = g_list_next(it))
+ if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
+
+ /* find the windows in the fullscreen layer, and mark them not-visited */
+ for (; it; it = g_list_next(it)) {
+ if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
+ else if (WINDOW_IS_CLIENT(it->data))
+ WINDOW_AS_CLIENT(it->data)->visited = FALSE;
+ }
+
+ client_calc_layer_internal(self);
+
+ /* skip over stuff above fullscreen layer */
+ for (it = stacking_list; it; it = g_list_next(it))
+ if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
+
+ /* now recalc any windows in the fullscreen layer which have not
+ had their layer recalced already */
+ for (; it; it = g_list_next(it)) {
+ if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
+ else if (WINDOW_IS_CLIENT(it->data) &&
+ !WINDOW_AS_CLIENT(it->data)->visited)
+ client_calc_layer_internal(it->data);
+ }
+}
+
+gboolean client_should_show(ObClient *self)
+{
+ if (self->iconic)
+ return FALSE;
+ if (client_normal(self) && screen_showing_desktop)
+ return FALSE;
+ if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
+ return TRUE;
+
+ return FALSE;
+}
+
+gboolean client_show(ObClient *self)
+{
+ gboolean show = FALSE;
+
+ if (client_should_show(self)) {
+ /* replay pending pointer event before showing the window, in case it
+ should be going to something under the window */
+ mouse_replay_pointer();
+
+ frame_show(self->frame);
+ show = TRUE;
+
+ /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
+ it needs to be in IconicState. This includes when it is on another
+ desktop!
+ */
+ client_change_wm_state(self);
+ }
+ return show;
+}
+
+gboolean client_hide(ObClient *self)
+{
+ gboolean hide = FALSE;
+
+ if (!client_should_show(self)) {
+ /* We don't need to ignore enter events here.
+ The window can hide/iconify in 3 different ways:
+ 1 - through an x message. in this case we ignore all enter events
+ caused by responding to the x message (unless underMouse)
+ 2 - by a keyboard action. in this case we ignore all enter events
+ caused by the action
+ 3 - by a mouse action. in this case they are doing stuff with the
+ mouse and focus _should_ move.
+
+ Also in action_end, we simulate an enter event that can't be ignored
+ so trying to ignore them is futile in case 3 anyways
+ */
+
+ /* replay pending pointer event before hiding the window, in case it
+ should be going to the window */
+ mouse_replay_pointer();
+
+ frame_hide(self->frame);
+ hide = TRUE;
+
+ /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
+ it needs to be in IconicState. This includes when it is on another
+ desktop!
+ */
+ client_change_wm_state(self);
+ }
+ return hide;
+}
+
+void client_showhide(ObClient *self)
+{
+ if (!client_show(self))
+ client_hide(self);
+}
+
+gboolean client_normal(ObClient *self) {
+ return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
+ self->type == OB_CLIENT_TYPE_DOCK ||
+ self->type == OB_CLIENT_TYPE_SPLASH);
+}
+
+gboolean client_helper(ObClient *self)
+{
+ return (self->type == OB_CLIENT_TYPE_UTILITY ||
+ self->type == OB_CLIENT_TYPE_MENU ||
+ self->type == OB_CLIENT_TYPE_TOOLBAR);
+}
+
+gboolean client_mouse_focusable(ObClient *self)
+{
+ return !(self->type == OB_CLIENT_TYPE_MENU ||
+ self->type == OB_CLIENT_TYPE_TOOLBAR ||
+ self->type == OB_CLIENT_TYPE_SPLASH ||
+ self->type == OB_CLIENT_TYPE_DOCK);
+}
+
+gboolean client_enter_focusable(ObClient *self)
+{
+ /* you can focus desktops but it shouldn't on enter */
+ return (client_mouse_focusable(self) &&
+ self->type != OB_CLIENT_TYPE_DESKTOP);
+}
+
+static void client_apply_startup_state(ObClient *self,
+ gint x, gint y, gint w, gint h)
+{
+ /* save the states that we are going to apply */
+ gboolean iconic = self->iconic;
+ gboolean fullscreen = self->fullscreen;
+ gboolean undecorated = self->undecorated;
+ gboolean shaded = self->shaded;
+ gboolean demands_attention = self->demands_attention;
+ gboolean max_horz = self->max_horz;
+ gboolean max_vert = self->max_vert;
+ Rect oldarea;
+ gint l;
+
+ /* turn them all off in the client, so they won't affect the window
+ being placed */
+ self->iconic = self->fullscreen = self->undecorated = self->shaded =
+ self->demands_attention = self->max_horz = self->max_vert = FALSE;
+
+ /* move the client to its placed position, or it it's already there,
+ generate a ConfigureNotify telling the client where it is.
+
+ do this after adjusting the frame. otherwise it gets all weird and
+ clients don't work right
+
+ do this before applying the states so they have the correct
+ pre-max/pre-fullscreen values
+ */
+ client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
+ ob_debug("placed window 0x%x at %d, %d with size %d x %d",
+ self->window, x, y, w, h);
+ /* save the area, and make it where it should be for the premax stuff */
+ oldarea = self->area;
+ RECT_SET(self->area, x, y, w, h);
+
+ /* apply the states. these are in a carefully crafted order.. */
+
+ if (iconic)
+ client_iconify(self, TRUE, FALSE, TRUE);
+ if (undecorated)
+ client_set_undecorated(self, TRUE);
+ if (shaded)
+ client_shade(self, TRUE);
+ if (demands_attention)
+ client_hilite(self, TRUE);
+
+ if (max_vert && max_horz)
+ client_maximize(self, TRUE, 0);
+ else if (max_vert)
+ client_maximize(self, TRUE, 2);
+ else if (max_horz)
+ client_maximize(self, TRUE, 1);
+
+ /* fullscreen removes the ability to apply other states */
+ if (fullscreen)
+ client_fullscreen(self, TRUE);
+
+ /* make sure client_setup_decor_and_functions() is called at least once */
+ client_setup_decor_and_functions(self, FALSE);
+
+ /* if the window hasn't been configured yet, then do so now, in fact the
+ x,y,w,h may _not_ be the same as the area rect, which can end up
+ meaning that the client isn't properly moved/resized by the fullscreen
+ function
+ pho can cause this because it maps at size of the screen but not 0,0
+ so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
+ then fullscreen'ing makes it go to 0,0 which it thinks it already is at
+ cuz thats where the pre-fullscreen will be. however the actual area is
+ not, so this needs to be called even if we have fullscreened/maxed
+ */
+ self->area = oldarea;
+ client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
+
+ /* nothing to do for the other states:
+ skip_taskbar
+ skip_pager
+ modal
+ above
+ below
+ */
+}
+
+void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
+{
+ /* these should be the current values. this is for when you're not moving,
+ just resizing */
+ g_assert(*x == self->area.x);
+ g_assert(oldw == self->area.width);
+
+ /* horizontal */
+ switch (self->gravity) {
+ default:
+ case NorthWestGravity:
+ case WestGravity:
+ case SouthWestGravity:
+ case StaticGravity:
+ case ForgetGravity:
+ break;
+ case NorthGravity:
+ case CenterGravity:
+ case SouthGravity:
+ *x -= (neww - oldw) / 2;
+ break;
+ case NorthEastGravity:
+ case EastGravity:
+ case SouthEastGravity:
+ *x -= neww - oldw;
+ break;
+ }
+}
+
+void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
+{
+ /* these should be the current values. this is for when you're not moving,
+ just resizing */
+ g_assert(*y == self->area.y);
+ g_assert(oldh == self->area.height);
+
+ /* vertical */
+ switch (self->gravity) {
+ default:
+ case NorthWestGravity:
+ case NorthGravity:
+ case NorthEastGravity:
+ case StaticGravity:
+ case ForgetGravity:
+ break;
+ case WestGravity:
+ case CenterGravity:
+ case EastGravity:
+ *y -= (newh - oldh) / 2;
+ break;
+ case SouthWestGravity:
+ case SouthGravity:
+ case SouthEastGravity:
+ *y -= newh - oldh;
+ break;
+ }
+}
+
+void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
+ gint *logicalw, gint *logicalh,
+ gboolean user)
+{
+ Rect desired = {*x, *y, *w, *h};
+ frame_rect_to_frame(self->frame, &desired);
+
+ /* make the frame recalculate its dimensions n shit without changing
+ anything visible for real, this way the constraints below can work with
+ the updated frame dimensions. */
+ frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
+
+ /* cap any X windows at the size of an unsigned short */
+ *w = MIN(*w,
+ (gint)G_MAXUSHORT
+ - self->frame->size.left - self->frame->size.right);
+ *h = MIN(*h,
+ (gint)G_MAXUSHORT
+ - self->frame->size.top - self->frame->size.bottom);
+
+ /* gets the frame's position */
+ frame_client_gravity(self->frame, x, y);
+
+ /* these positions are frame positions, not client positions */
+
+ /* set the size and position if fullscreen */
+ if (self->fullscreen) {
+ const Rect *a;
+ guint i;
+
+ i = screen_find_monitor(&desired);
+ a = screen_physical_area_monitor(i);
+
+ *x = a->x;
+ *y = a->y;
+ *w = a->width;
+ *h = a->height;
+
+ user = FALSE; /* ignore if the client can't be moved/resized when it
+ is fullscreening */
+ } else if (self->max_horz || self->max_vert) {
+ Rect *a;
+ guint i;
+
+ /* use all possible struts when maximizing to the full screen */
+ i = screen_find_monitor(&desired);
+ a = screen_area(self->desktop, i,
+ (self->max_horz && self->max_vert ? NULL : &desired));
+
+ /* set the size and position if maximized */
+ if (self->max_horz) {
+ *x = a->x;
+ *w = a->width - self->frame->size.left - self->frame->size.right;
+ }
+ if (self->max_vert) {
+ *y = a->y;
+ *h = a->height - self->frame->size.top - self->frame->size.bottom;
+ }
+
+ user = FALSE; /* ignore if the client can't be moved/resized when it
+ is maximizing */
+
+ g_slice_free(Rect, a);
+ }
+
+ /* gets the client's position */
+ frame_frame_gravity(self->frame, x, y);
+
+ /* work within the preferred sizes given by the window, these may have
+ changed rather than it's requested width and height, so always run
+ through this code */
+ {
+ gint basew, baseh, minw, minh;
+ gint incw, inch, maxw, maxh;
+ gfloat minratio, maxratio;
+
+ incw = self->size_inc.width;
+ inch = self->size_inc.height;
+ minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
+ 0 : self->min_ratio;
+ maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
+ 0 : self->max_ratio;
+
+ /* base size is substituted with min size if not specified */
+ if (self->base_size.width >= 0 || self->base_size.height >= 0) {
+ basew = self->base_size.width;
+ baseh = self->base_size.height;
+ } else {
+ basew = self->min_size.width;
+ baseh = self->min_size.height;
+ }
+ /* min size is substituted with base size if not specified */
+ if (self->min_size.width || self->min_size.height) {
+ minw = self->min_size.width;
+ minh = self->min_size.height;
+ } else {
+ minw = self->base_size.width;
+ minh = self->base_size.height;
+ }
+
+ /* This comment is no longer true */
+ /* if this is a user-requested resize, then check against min/max
+ sizes */
+
+ /* smaller than min size or bigger than max size? */
+ if (*w > self->max_size.width) *w = self->max_size.width;
+ if (*w < minw) *w = minw;
+ if (*h > self->max_size.height) *h = self->max_size.height;
+ if (*h < minh) *h = minh;
+
+ *w -= basew;
+ *h -= baseh;
+
+ /* the sizes to used for maximized */
+ maxw = *w;
+ maxh = *h;
+
+ /* keep to the increments */
+ *w /= incw;
+ *h /= inch;
+
+ /* you cannot resize to nothing */
+ if (basew + *w < 1) *w = 1 - basew;
+ if (baseh + *h < 1) *h = 1 - baseh;
+
+ /* save the logical size */
+ *logicalw = incw > 1 ? *w : *w + basew;
+ *logicalh = inch > 1 ? *h : *h + baseh;
+
+ *w *= incw;
+ *h *= inch;
+
+ /* if maximized/fs then don't use the size increments */
+ if (self->fullscreen || self->max_horz) *w = maxw;
+ if (self->fullscreen || self->max_vert) *h = maxh;
+
+ *w += basew;
+ *h += baseh;
+
+ /* adjust the height to match the width for the aspect ratios.
+ for this, min size is not substituted for base size ever. */
+ *w -= self->base_size.width;
+ *h -= self->base_size.height;
+
+ if (minratio)
+ if (*h * minratio > *w) {
+ *h = (gint)(*w / minratio);
+
+ /* you cannot resize to nothing */
+ if (*h < 1) {
+ *h = 1;
+ *w = (gint)(*h * minratio);
+ }
+ }
+ if (maxratio)
+ if (*h * maxratio < *w) {
+ *h = (gint)(*w / maxratio);
+
+ /* you cannot resize to nothing */
+ if (*h < 1) {
+ *h = 1;
+ *w = (gint)(*h * minratio);
+ }
+ }
+
+ *w += self->base_size.width;
+ *h += self->base_size.height;
+ }
+
+ /* these override the above states! if you cant move you can't move! */
+ if (user) {
+ if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
+ *x = self->area.x;
+ *y = self->area.y;
+ }
+ if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
+ *w = self->area.width;
+ *h = self->area.height;
+ }
+ }
+
+ g_assert(*w > 0);
+ g_assert(*h > 0);
+}
+
+void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
+ gboolean user, gboolean final, gboolean force_reply)
+{
+ Rect oldframe, oldclient;
+ gboolean send_resize_client;
+ gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
+ gboolean fmoved, fresized;
+ guint fdecor = self->frame->decorations;
+ gboolean fhorz = self->frame->max_horz;
+ gboolean fvert = self->frame->max_vert;
+ gint logicalw, logicalh;
+
+ /* find the new x, y, width, and height (and logical size) */
+ client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
+
+ /* set the logical size if things changed */
+ if (!(w == self->area.width && h == self->area.height))
+ SIZE_SET(self->logical_size, logicalw, logicalh);
+
+ /* figure out if we moved or resized or what */
+ moved = (x != self->area.x || y != self->area.y);
+ resized = (w != self->area.width || h != self->area.height);
+
+ oldframe = self->frame->area;
+ oldclient = self->area;
+ RECT_SET(self->area, x, y, w, h);
+
+ /* for app-requested resizes, always resize if 'resized' is true.
+ for user-requested ones, only resize if final is true, or when
+ resizing in redraw mode */
+ send_resize_client = ((!user && resized) ||
+ (user && (final ||
+ (resized && config_resize_redraw))));
+
+ /* if the client is enlarging, then resize the client before the frame */
+ if (send_resize_client && (w > oldclient.width || h > oldclient.height)) {
+ XMoveResizeWindow(obt_display, self->window,
+ self->frame->size.left, self->frame->size.top,
+ MAX(w, oldclient.width), MAX(h, oldclient.height));
+ frame_adjust_client_area(self->frame);
+ }
+
+ /* find the frame's dimensions and move/resize it */
+ fmoved = moved;
+ fresized = resized;
+
+ /* if decorations changed, then readjust everything for the frame */
+ if (self->decorations != fdecor ||
+ self->max_horz != fhorz || self->max_vert != fvert)
+ {
+ fmoved = fresized = TRUE;
+ }
+
+ /* adjust the frame */
+ if (fmoved || fresized) {
+ gulong ignore_start;
+ if (!user)
+ ignore_start = event_start_ignore_all_enters();
+
+ /* replay pending pointer event before move the window, in case it
+ would change what window gets the event */
+ mouse_replay_pointer();
+
+ frame_adjust_area(self->frame, fmoved, fresized, FALSE);
+
+ if (!user)
+ event_end_ignore_all_enters(ignore_start);
+ }
+
+ if (!user || final) {
+ gint oldrx = self->root_pos.x;
+ gint oldry = self->root_pos.y;
+ /* we have reset the client to 0 border width, so don't include
+ it in these coords */
+ POINT_SET(self->root_pos,
+ self->frame->area.x + self->frame->size.left -
+ self->border_width,
+ self->frame->area.y + self->frame->size.top -
+ self->border_width);
+ if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
+ rootmoved = TRUE;
+ }
+
+ /* This is kinda tricky and should not be changed.. let me explain!
+
+ When user = FALSE, then the request is coming from the application
+ itself, and we are more strict about when to send a synthetic
+ ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
+ in this case (or send one if force_reply is true)
+
+ When user = TRUE, then the request is coming from "us", like when we
+ maximize a window or something. In this case we are more lenient. We
+ used to follow the same rules as above, but _Java_ Swing can't handle
+ this. So just to appease Swing, when user = TRUE, we always send
+ a synthetic ConfigureNotify to give the window its root coordinates.
+ Lastly, if force_reply is TRUE, we always send a
+ ConfigureNotify, which is needed during a resize with XSYNCronization.
+ */
+ if ((!user && !resized && (rootmoved || force_reply)) ||
+ (user && ((!resized && force_reply) || (final && rootmoved))))
+ {
+ XEvent event;
+
+ event.type = ConfigureNotify;
+ event.xconfigure.display = obt_display;
+ event.xconfigure.event = self->window;
+ event.xconfigure.window = self->window;
+
+ ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
+ self->title, self->root_pos.x, self->root_pos.y, w, h);
+
+ /* root window real coords */
+ event.xconfigure.x = self->root_pos.x;
+ event.xconfigure.y = self->root_pos.y;
+ event.xconfigure.width = w;
+ event.xconfigure.height = h;
+ event.xconfigure.border_width = self->border_width;
+ event.xconfigure.above = None;
+ event.xconfigure.override_redirect = FALSE;
+ XSendEvent(event.xconfigure.display, event.xconfigure.window,
+ FALSE, StructureNotifyMask, &event);
+ }
+
+ /* if the client is shrinking, then resize the frame before the client.
+
+ both of these resize sections may run, because the top one only resizes
+ in the direction that is growing
+ */
+ if (send_resize_client && (w <= oldclient.width || h <= oldclient.height))
+ {
+ frame_adjust_client_area(self->frame);
+ XMoveResizeWindow(obt_display, self->window,
+ self->frame->size.left, self->frame->size.top, w, h);
+ }
+
+ XFlush(obt_display);
+
+ /* if it moved between monitors, then this can affect the stacking
+ layer of this window or others - for fullscreen windows.
+ also if it changed to/from oldschool fullscreen then its layer may
+ change
+
+ watch out tho, don't try change stacking stuff if the window is no
+ longer being managed !
+ */
+ if (self->managed &&
+ (screen_find_monitor(&self->frame->area) !=
+ screen_find_monitor(&oldframe) ||
+ (final && (client_is_oldfullscreen(self, &oldclient) !=
+ client_is_oldfullscreen(self, &self->area)))))
+ {
+ client_calc_layer(self);
+ }
+}
+
+void client_fullscreen(ObClient *self, gboolean fs)
+{
+ gint x, y, w, h;
+
+ if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
+ self->fullscreen == fs) return; /* already done */
+
+ self->fullscreen = fs;
+ client_change_state(self); /* change the state hints on the client */
+
+ if (fs) {
+ self->pre_fullscreen_area = self->area;
+ self->pre_fullscreen_max_horz = self->max_horz;
+ self->pre_fullscreen_max_vert = self->max_vert;
+
+ /* if the window is maximized, its area isn't all that meaningful.
+ save its premax area instead. */
+ if (self->max_horz) {
+ self->pre_fullscreen_area.x = self->pre_max_area.x;
+ self->pre_fullscreen_area.width = self->pre_max_area.width;
+ }
+ if (self->max_vert) {
+ self->pre_fullscreen_area.y = self->pre_max_area.y;
+ self->pre_fullscreen_area.height = self->pre_max_area.height;
+ }
+
+ /* these will help configure_full figure out where to fullscreen
+ the window */
+ x = self->area.x;
+ y = self->area.y;
+ w = self->area.width;
+ h = self->area.height;
+ } else {
+ g_assert(self->pre_fullscreen_area.width > 0 &&
+ self->pre_fullscreen_area.height > 0);
+
+ self->max_horz = self->pre_fullscreen_max_horz;
+ self->max_vert = self->pre_fullscreen_max_vert;
+ if (self->max_horz) {
+ self->pre_max_area.x = self->pre_fullscreen_area.x;
+ self->pre_max_area.width = self->pre_fullscreen_area.width;
+ }
+ if (self->max_vert) {
+ self->pre_max_area.y = self->pre_fullscreen_area.y;
+ self->pre_max_area.height = self->pre_fullscreen_area.height;
+ }
+
+ x = self->pre_fullscreen_area.x;
+ y = self->pre_fullscreen_area.y;
+ w = self->pre_fullscreen_area.width;
+ h = self->pre_fullscreen_area.height;
+ RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
+ }
+
+ ob_debug("Window %s going fullscreen (%d)",
+ self->title, self->fullscreen);
+
+ if (fs) {
+ /* make sure the window is on some monitor */
+ client_find_onscreen(self, &x, &y, w, h, FALSE);
+ }
+
+ client_setup_decor_and_functions(self, FALSE);
+ client_move_resize(self, x, y, w, h);
+
+ /* and adjust our layer/stacking. do this after resizing the window,
+ and applying decorations, because windows which fill the screen are
+ considered "fullscreen" and it affects their layer */
+ client_calc_layer(self);
+
+ if (fs) {
+ /* try focus us when we go into fullscreen mode */
+ client_focus(self);
+ }
+}
+
+static void client_iconify_recursive(ObClient *self,
+ gboolean iconic, gboolean curdesk,
+ gboolean hide_animation)
+{
+ GSList *it;
+ gboolean changed = FALSE;
+
+ if (self->iconic != iconic) {
+ ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
+ self->window);
+
+ if (iconic) {
+ /* don't let non-normal windows iconify along with their parents
+ or whatever */
+ if (client_normal(self)) {
+ self->iconic = iconic;
+
+ /* update the focus lists.. iconic windows go to the bottom of
+ the list. this will also call focus_cycle_addremove(). */
+ focus_order_to_bottom(self);
+
+ changed = TRUE;
+ }
+ } else {
+ self->iconic = iconic;
+
+ if (curdesk && self->desktop != screen_desktop &&
+ self->desktop != DESKTOP_ALL)
+ client_set_desktop(self, screen_desktop, FALSE, FALSE);
+
+ /* this puts it after the current focused window, this will
+ also cause focus_cycle_addremove() to be called for the
+ client */
+ focus_order_like_new(self);
+
+ changed = TRUE;
+ }
+ }
+
+ if (changed) {
+ client_change_state(self);
+ if (config_animate_iconify && !hide_animation)
+ frame_begin_iconify_animation(self->frame, iconic);
+ /* do this after starting the animation so it doesn't flash */
+ client_showhide(self);
+ }
+
+ /* iconify all direct transients, and deiconify all transients
+ (non-direct too) */
+ for (it = self->transients; it; it = g_slist_next(it))
+ if (it->data != self)
+ if (client_is_direct_child(self, it->data) || !iconic)
+ client_iconify_recursive(it->data, iconic, curdesk,
+ hide_animation);
+}
+
+void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
+ gboolean hide_animation)
+{
+ if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
+ /* move up the transient chain as far as possible first */
+ self = client_search_top_direct_parent(self);
+ client_iconify_recursive(self, iconic, curdesk, hide_animation);
+ }
+}
+
+void client_maximize(ObClient *self, gboolean max, gint dir)
+{
+ gint x, y, w, h;
+
+ g_assert(dir == 0 || dir == 1 || dir == 2);
+ if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
+
+ /* check if already done */
+ if (max) {
+ if (dir == 0 && self->max_horz && self->max_vert) return;
+ if (dir == 1 && self->max_horz) return;
+ if (dir == 2 && self->max_vert) return;
+ } else {
+ if (dir == 0 && !self->max_horz && !self->max_vert) return;
+ if (dir == 1 && !self->max_horz) return;
+ if (dir == 2 && !self->max_vert) return;
+ }
+
+ /* these will help configure_full figure out which screen to fill with
+ the window */
+ x = self->area.x;
+ y = self->area.y;
+ w = self->area.width;
+ h = self->area.height;
+
+ if (max) {
+ if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
+ RECT_SET(self->pre_max_area,
+ self->area.x, self->pre_max_area.y,
+ self->area.width, self->pre_max_area.height);
+ }
+ if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
+ RECT_SET(self->pre_max_area,
+ self->pre_max_area.x, self->area.y,
+ self->pre_max_area.width, self->area.height);
+ }
+ } else {
+ if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
+ g_assert(self->pre_max_area.width > 0);
+
+ x = self->pre_max_area.x;
+ w = self->pre_max_area.width;
+
+ RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
+ 0, self->pre_max_area.height);
+ }
+ if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
+ g_assert(self->pre_max_area.height > 0);
+
+ y = self->pre_max_area.y;
+ h = self->pre_max_area.height;
+
+ RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
+ self->pre_max_area.width, 0);
+ }
+ }
+
+ if (dir == 0 || dir == 1) /* horz */
+ self->max_horz = max;
+ if (dir == 0 || dir == 2) /* vert */
+ self->max_vert = max;
+
+ if (max) {
+ /* make sure the window is on some monitor */
+ client_find_onscreen(self, &x, &y, w, h, FALSE);
+ }
+
+ client_change_state(self); /* change the state hints on the client */
+
+ client_setup_decor_and_functions(self, FALSE);
+ client_move_resize(self, x, y, w, h);
+}
+
+void client_shade(ObClient *self, gboolean shade)
+{
+ if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
+ shade) || /* can't shade */
+ self->shaded == shade) return; /* already done */
+
+ self->shaded = shade;
+ client_change_state(self);
+ client_change_wm_state(self); /* the window is being hidden/shown */
+ /* resize the frame to just the titlebar */
+ frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
+}
+
+static void client_ping_event(ObClient *self, gboolean dead)
+{
+ if (self->not_responding != dead) {
+ self->not_responding = dead;
+ client_update_title(self);
+
+ if (dead)
+ /* the client isn't responding, so ask to kill it */
+ client_prompt_kill(self);
+ else {
+ /* it came back to life ! */
+
+ if (self->kill_prompt) {
+ prompt_unref(self->kill_prompt);
+ self->kill_prompt = NULL;
+ }
+
+ self->kill_level = 0;
+ }
+ }
+}
+
+void client_close(ObClient *self)
+{
+ if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
+
+ /* if closing an internal obprompt, that is just cancelling it */
+ if (self->prompt) {
+ prompt_cancel(self->prompt);
+ return;
+ }
+
+ /* in the case that the client provides no means to requesting that it
+ close, we just kill it */
+ if (!self->delete_window)
+ /* don't use client_kill(), we should only kill based on PID in
+ response to a lack of PING replies */
+ XKillClient(obt_display, self->window);
+ else {
+ /* request the client to close with WM_DELETE_WINDOW */
+ OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS,
+ OBT_PROP_ATOM(WM_DELETE_WINDOW), event_time(),
+ 0, 0, 0, NoEventMask);
+
+ /* we're trying to close the window, so see if it is responding. if it
+ is not, then we will let them kill the window */
+ if (self->ping)
+ ping_start(self, client_ping_event);
+
+ /* if we already know the window isn't responding (maybe they clicked
+ no in the kill dialog but it hasn't come back to life), then show
+ the kill dialog */
+ if (self->not_responding)
+ client_prompt_kill(self);
+ }
+}
+
+#define OB_KILL_RESULT_NO 0
+#define OB_KILL_RESULT_YES 1
+
+static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
+{
+ ObClient *self = data;
+
+ if (result == OB_KILL_RESULT_YES)
+ client_kill(self);
+ return TRUE; /* call the cleanup func */
+}
+
+static void client_kill_cleanup(ObPrompt *p, gpointer data)
+{
+ ObClient *self = data;
+
+ g_assert(p == self->kill_prompt);
+
+ prompt_unref(self->kill_prompt);
+ self->kill_prompt = NULL;
+}
+
+static void client_prompt_kill(ObClient *self)
+{
+ /* check if we're already prompting */
+ if (!self->kill_prompt) {
+ ObPromptAnswer answers[] = {
+ { 0, OB_KILL_RESULT_NO },
+ { 0, OB_KILL_RESULT_YES }
+ };
+ gchar *m;
+ const gchar *y, *title;
+
+ title = self->original_title;
+ if (title[0] == '\0') {
+ /* empty string, so use its parent */
+ ObClient *p = client_search_top_direct_parent(self);
+ if (p) title = p->original_title;
+ }
+
+ if (client_on_localhost(self)) {
+ const gchar *sig;
+
+ if (self->kill_level == 0)
+ sig = "terminate";
+ else
+ sig = "kill";
+
+ m = g_strdup_printf
+ (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"),
+ title, sig);
+ y = _("End Process");
+ }
+ else {
+ m = g_strdup_printf
+ (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"),
+ title);
+ y = _("Disconnect");
+ }
+ /* set the dialog buttons' text */
+ answers[0].text = _("Cancel"); /* "no" */
+ answers[1].text = y; /* "yes" */
+
+ self->kill_prompt = prompt_new(m, NULL, answers,
+ sizeof(answers)/sizeof(answers[0]),
+ OB_KILL_RESULT_NO, /* default = no */
+ OB_KILL_RESULT_NO, /* cancel = no */
+ client_kill_requested,
+ client_kill_cleanup,
+ self);
+ g_free(m);
+ }
+
+ prompt_show(self->kill_prompt, self, TRUE);
+}
+
+void client_kill(ObClient *self)
+{
+ /* don't kill our own windows */
+ if (self->prompt) return;
+
+ if (client_on_localhost(self) && self->pid) {
+ /* running on the local host */
+ if (self->kill_level == 0) {
+ ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
+ self->window, self->pid);
+ kill(self->pid, SIGTERM);
+ ++self->kill_level;
+
+ /* show that we're trying to kill it */
+ client_update_title(self);
+ }
+ else {
+ ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
+ self->window, self->pid);
+ kill(self->pid, SIGKILL); /* kill -9 */
+ }
+ }
+ else {
+ /* running on a remote host */
+ XKillClient(obt_display, self->window);
+ }
+}
+
+void client_hilite(ObClient *self, gboolean hilite)
+{
+ if (self->demands_attention == hilite)
+ return; /* no change */
+
+ /* don't allow focused windows to hilite */
+ self->demands_attention = hilite && !client_focused(self);
+ if (self->frame != NULL) { /* if we're mapping, just set the state */
+ if (self->demands_attention) {
+ frame_flash_start(self->frame);
+
+ /* if the window is on another desktop then raise it and make it
+ the most recently used window */
+ if (self->desktop != screen_desktop &&
+ self->desktop != DESKTOP_ALL)
+ {
+ stacking_raise(CLIENT_AS_WINDOW(self));
+ focus_order_to_top(self);
+ }
+ }
+ else
+ frame_flash_stop(self->frame);
+ client_change_state(self);
+ }
+}
+
+static void client_set_desktop_recursive(ObClient *self,
+ guint target,
+ gboolean donthide,
+ gboolean dontraise)
+{
+ guint old;
+ GSList *it;
+
+ if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
+
+ ob_debug("Setting desktop %u", target+1);
+
+ g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
+
+ old = self->desktop;
+ self->desktop = target;
+ OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, target);
+ /* the frame can display the current desktop state */
+ frame_adjust_state(self->frame);
+ /* 'move' the window to the new desktop */
+ if (!donthide)
+ client_hide(self);
+ client_show(self);
+ /* raise if it was not already on the desktop */
+ if (old != DESKTOP_ALL && !dontraise)
+ stacking_raise(CLIENT_AS_WINDOW(self));
+ if (STRUT_EXISTS(self->strut))
+ screen_update_areas();
+ else
+ /* the new desktop's geometry may be different, so we may need to
+ resize, for example if we are maximized */
+ client_reconfigure(self, FALSE);
+
+ focus_cycle_addremove(self, FALSE);
+ }
+
+ /* move all transients */
+ for (it = self->transients; it; it = g_slist_next(it))
+ if (it->data != self)
+ if (client_is_direct_child(self, it->data))
+ client_set_desktop_recursive(it->data, target,
+ donthide, dontraise);
+}
+
+void client_set_desktop(ObClient *self, guint target,
+ gboolean donthide, gboolean dontraise)
+{
+ self = client_search_top_direct_parent(self);
+ client_set_desktop_recursive(self, target, donthide, dontraise);
+
+ focus_cycle_addremove(NULL, TRUE);
+}
+
+gboolean client_is_direct_child(ObClient *parent, ObClient *child)
+{
+ while (child != parent && (child = client_direct_parent(child)));
+ return child == parent;
+}
+
+ObClient *client_search_modal_child(ObClient *self)
+{
+ GSList *it;
+ ObClient *ret;
+
+ for (it = self->transients; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+ if ((ret = client_search_modal_child(c))) return ret;
+ if (c->modal) return c;
+ }
+ return NULL;
+}
+
+struct ObClientFindDestroyUnmap {
+ Window window;
+ gint ignore_unmaps;
+};
+
+static gboolean find_destroy_unmap(XEvent *e, gpointer data)
+{
+ struct ObClientFindDestroyUnmap *find = data;
+ if (e->type == DestroyNotify)
+ return e->xdestroywindow.window == find->window;
+ if (e->type == UnmapNotify && e->xunmap.window == find->window)
+ /* ignore the first $find->ignore_unmaps$ many unmap events */
+ return --find->ignore_unmaps < 0;
+ return FALSE;
+}
+
+gboolean client_validate(ObClient *self)
+{
+ struct ObClientFindDestroyUnmap find;
+
+ XSync(obt_display, FALSE); /* get all events on the server */
+
+ find.window = self->window;
+ find.ignore_unmaps = self->ignore_unmaps;
+ if (xqueue_exists_local(find_destroy_unmap, &find))
+ return FALSE;
+
+ return TRUE;
+}
+
+void client_set_wm_state(ObClient *self, glong state)
+{
+ if (state == self->wmstate) return; /* no change */
+
+ switch (state) {
+ case IconicState:
+ client_iconify(self, TRUE, TRUE, FALSE);
+ break;
+ case NormalState:
+ client_iconify(self, FALSE, TRUE, FALSE);
+ break;
+ }
+}
+
+void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
+{
+ gboolean shaded = self->shaded;
+ gboolean fullscreen = self->fullscreen;
+ gboolean undecorated = self->undecorated;
+ gboolean max_horz = self->max_horz;
+ gboolean max_vert = self->max_vert;
+ gboolean modal = self->modal;
+ gboolean iconic = self->iconic;
+ gboolean demands_attention = self->demands_attention;
+ gboolean above = self->above;
+ gboolean below = self->below;
+ gint i;
+ gboolean value;
+
+ if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
+ action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
+ action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)))
+ /* an invalid action was passed to the client message, ignore it */
+ return;
+
+ for (i = 0; i < 2; ++i) {
+ Atom state = i == 0 ? data1 : data2;
+
+ if (!state) continue;
+
+ /* if toggling, then pick whether we're adding or removing */
+ if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
+ if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
+ value = modal;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
+ value = self->max_vert;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
+ value = self->max_horz;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
+ value = shaded;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
+ value = self->skip_taskbar;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
+ value = self->skip_pager;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
+ value = self->iconic;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
+ value = fullscreen;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
+ value = self->above;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
+ value = self->below;
+ else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
+ value = self->demands_attention;
+ else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
+ value = undecorated;
+ else
+ g_assert_not_reached();
+ action = value ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+ OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ }
+
+ value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
+ modal = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
+ max_vert = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
+ max_horz = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
+ shaded = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
+ self->skip_taskbar = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
+ self->skip_pager = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
+ iconic = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
+ fullscreen = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
+ above = value;
+ /* only unset below when setting above, otherwise you can't get to
+ the normal layer */
+ if (value)
+ below = FALSE;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
+ /* and vice versa */
+ if (value)
+ above = FALSE;
+ below = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
+ demands_attention = value;
+ } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
+ undecorated = value;
+ }
+ }
+
+ if (max_horz != self->max_horz || max_vert != self->max_vert) {
+ if (max_horz != self->max_horz && max_vert != self->max_vert) {
+ /* toggling both */
+ if (max_horz == max_vert) { /* both going the same way */
+ client_maximize(self, max_horz, 0);
+ } else {
+ client_maximize(self, max_horz, 1);
+ client_maximize(self, max_vert, 2);
+ }
+ } else {
+ /* toggling one */
+ if (max_horz != self->max_horz)
+ client_maximize(self, max_horz, 1);
+ else
+ client_maximize(self, max_vert, 2);
+ }
+ }
+ /* change fullscreen state before shading, as it will affect if the window
+ can shade or not */
+ if (fullscreen != self->fullscreen)
+ client_fullscreen(self, fullscreen);
+ if (shaded != self->shaded)
+ client_shade(self, shaded);
+ if (undecorated != self->undecorated)
+ client_set_undecorated(self, undecorated);
+ if (above != self->above || below != self->below) {
+ self->above = above;
+ self->below = below;
+ client_calc_layer(self);
+ }
+
+ if (modal != self->modal) {
+ self->modal = modal;
+ /* when a window changes modality, then its stacking order with its
+ transients needs to change */
+ stacking_raise(CLIENT_AS_WINDOW(self));
+
+ /* it also may get focused. if something is focused that shouldn't
+ be focused anymore, then move the focus */
+ if (focus_client && client_focus_target(focus_client) != focus_client)
+ client_focus(focus_client);
+ }
+
+ if (iconic != self->iconic)
+ client_iconify(self, iconic, FALSE, FALSE);
+
+ if (demands_attention != self->demands_attention)
+ client_hilite(self, demands_attention);
+
+ client_change_state(self); /* change the hint to reflect these changes */
+
+ focus_cycle_addremove(self, TRUE);
+}
+
+ObClient *client_focus_target(ObClient *self)
+{
+ ObClient *child = NULL;
+
+ child = client_search_modal_child(self);
+ if (child) return child;
+ return self;
+}
+
+gboolean client_can_focus(ObClient *self)
+{
+ /* choose the correct target */
+ self = client_focus_target(self);
+
+ if (!self->frame->visible)
+ return FALSE;
+
+ if (!(self->can_focus || self->focus_notify))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean client_focus(ObClient *self)
+{
+ if (!client_validate(self)) return FALSE;
+
+ /* we might not focus this window, so if we have modal children which would
+ be focused instead, bring them to this desktop */
+ client_bring_modal_windows(self);
+
+ /* choose the correct target */
+ self = client_focus_target(self);
+
+ if (!client_can_focus(self)) {
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Client %s can't be focused", self->title);
+ return FALSE;
+ }
+
+ /* if we have helper windows they should be there with the window */
+ client_bring_helper_windows(self);
+
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focusing client \"%s\" (0x%x) at time %u",
+ self->title, self->window, event_time());
+
+ /* if using focus_delay, stop the timer now so that focus doesn't
+ go moving on us */
+ event_halt_focus_delay();
+
+ obt_display_ignore_errors(TRUE);
+
+ if (self->can_focus) {
+ /* This can cause a BadMatch error with CurrentTime, or if an app
+ passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
+ XSetInputFocus(obt_display, self->window, RevertToPointerRoot,
+ event_time());
+ }
+
+ if (self->focus_notify) {
+ XEvent ce;
+ ce.xclient.type = ClientMessage;
+ ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
+ ce.xclient.display = obt_display;
+ ce.xclient.window = self->window;
+ ce.xclient.format = 32;
+ ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
+ ce.xclient.data.l[1] = event_time();
+ ce.xclient.data.l[2] = 0l;
+ ce.xclient.data.l[3] = 0l;
+ ce.xclient.data.l[4] = 0l;
+ XSendEvent(obt_display, self->window, FALSE, NoEventMask, &ce);
+ }
+
+ obt_display_ignore_errors(FALSE);
+
+ ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d",
+ obt_display_error_occured);
+ return !obt_display_error_occured;
+}
+
+static void client_present(ObClient *self, gboolean here, gboolean raise,
+ gboolean unshade)
+{
+ if (client_normal(self) && screen_showing_desktop)
+ screen_show_desktop(FALSE, self);
+ if (self->iconic)
+ client_iconify(self, FALSE, here, FALSE);
+ if (self->desktop != DESKTOP_ALL &&
+ self->desktop != screen_desktop)
+ {
+ if (here)
+ client_set_desktop(self, screen_desktop, FALSE, TRUE);
+ else
+ screen_set_desktop(self->desktop, FALSE);
+ } else if (!self->frame->visible)
+ /* if its not visible for other reasons, then don't mess
+ with it */
+ return;
+ if (self->shaded && unshade)
+ client_shade(self, FALSE);
+ if (raise)
+ stacking_raise(CLIENT_AS_WINDOW(self));
+
+ client_focus(self);
+}
+
+/* this function exists to map to the net_active_window message in the ewmh */
+void client_activate(ObClient *self, gboolean desktop,
+ gboolean here, gboolean raise,
+ gboolean unshade, gboolean user)
+{
+ self = client_focus_target(self);
+
+ if (client_can_steal_focus(self, desktop, user, event_time(), CurrentTime))
+ client_present(self, here, raise, unshade);
+ else
+ client_hilite(self, TRUE);
+}
+
+static void client_bring_windows_recursive(ObClient *self,
+ guint desktop,
+ gboolean helpers,
+ gboolean modals,
+ gboolean iconic)
+{
+ GSList *it;
+
+ for (it = self->transients; it; it = g_slist_next(it))
+ client_bring_windows_recursive(it->data, desktop,
+ helpers, modals, iconic);
+
+ if (((helpers && client_helper(self)) ||
+ (modals && self->modal)) &&
+ (!screen_compare_desktops(self->desktop, desktop) ||
+ (iconic && self->iconic)))
+ {
+ if (iconic && self->iconic)
+ client_iconify(self, FALSE, TRUE, FALSE);
+ else
+ client_set_desktop(self, desktop, FALSE, FALSE);
+ }
+}
+
+void client_bring_helper_windows(ObClient *self)
+{
+ client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
+}
+
+void client_bring_modal_windows(ObClient *self)
+{
+ client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
+}
+
+gboolean client_focused(ObClient *self)
+{
+ return self == focus_client;
+}
+
+RrImage* client_icon(ObClient *self)
+{
+ RrImage *ret = NULL;
+
+ if (self->icon_set)
+ ret = self->icon_set;
+ else if (self->parents) {
+ GSList *it;
+ for (it = self->parents; it && !ret; it = g_slist_next(it))
+ ret = client_icon(it->data);
+ }
+ if (!ret)
+ ret = client_default_icon;
+ return ret;
+}
+
+void client_set_layer(ObClient *self, gint layer)
+{
+ if (layer < 0) {
+ self->below = TRUE;
+ self->above = FALSE;
+ } else if (layer == 0) {
+ self->below = self->above = FALSE;
+ } else {
+ self->below = FALSE;
+ self->above = TRUE;
+ }
+ client_calc_layer(self);
+ client_change_state(self); /* reflect this in the state hints */
+}
+
+void client_set_undecorated(ObClient *self, gboolean undecorated)
+{
+ if (self->undecorated != undecorated &&
+ /* don't let it undecorate if the function is missing, but let
+ it redecorate */
+ (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
+ {
+ self->undecorated = undecorated;
+ client_setup_decor_and_functions(self, TRUE);
+ client_change_state(self); /* reflect this in the state hints */
+ }
+}
+
+guint client_monitor(ObClient *self)
+{
+ return screen_find_monitor(&self->frame->area);
+}
+
+ObClient *client_direct_parent(ObClient *self)
+{
+ if (!self->parents) return NULL;
+ if (self->transient_for_group) return NULL;
+ return self->parents->data;
+}
+
+ObClient *client_search_top_direct_parent(ObClient *self)
+{
+ ObClient *p;
+ while ((p = client_direct_parent(self))) self = p;
+ return self;
+}
+
+static GSList *client_search_all_top_parents_internal(ObClient *self,
+ gboolean bylayer,
+ ObStackingLayer layer)
+{
+ GSList *ret;
+ ObClient *p;
+
+ /* move up the direct transient chain as far as possible */
+ while ((p = client_direct_parent(self)) &&
+ (!bylayer || p->layer == layer))
+ self = p;
+
+ if (!self->parents)
+ ret = g_slist_prepend(NULL, self);
+ else
+ ret = g_slist_copy(self->parents);
+
+ return ret;
+}
+
+GSList *client_search_all_top_parents(ObClient *self)
+{
+ return client_search_all_top_parents_internal(self, FALSE, 0);
+}
+
+GSList *client_search_all_top_parents_layer(ObClient *self)
+{
+ return client_search_all_top_parents_internal(self, TRUE, self->layer);
+}
+
+ObClient *client_search_focus_parent(ObClient *self)
+{
+ GSList *it;
+
+ for (it = self->parents; it; it = g_slist_next(it))
+ if (client_focused(it->data)) return it->data;
+
+ return NULL;
+}
+
+ObClient *client_search_focus_parent_full(ObClient *self)
+{
+ GSList *it;
+ ObClient *ret = NULL;
+
+ for (it = self->parents; it; it = g_slist_next(it)) {
+ if (client_focused(it->data))
+ ret = it->data;
+ else
+ ret = client_search_focus_parent_full(it->data);
+ if (ret) break;
+ }
+ return ret;
+}
+
+ObClient *client_search_parent(ObClient *self, ObClient *search)
+{
+ GSList *it;
+
+ for (it = self->parents; it; it = g_slist_next(it))
+ if (it->data == search) return search;
+
+ return NULL;
+}
+
+ObClient *client_search_transient(ObClient *self, ObClient *search)
+{
+ GSList *sit;
+
+ for (sit = self->transients; sit; sit = g_slist_next(sit)) {
+ if (sit->data == search)
+ return search;
+ if (client_search_transient(sit->data, search))
+ return search;
+ }
+ return NULL;
+}
+
+static void detect_edge(Rect area, ObDirection dir,
+ gint my_head, gint my_size,
+ gint my_edge_start, gint my_edge_size,
+ gint *dest, gboolean *near_edge)
+{
+ gint edge_start, edge_size, head, tail;
+ gboolean skip_head = FALSE, skip_tail = FALSE;
+
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ case OB_DIRECTION_SOUTH:
+ edge_start = area.x;
+ edge_size = area.width;
+ break;
+ case OB_DIRECTION_EAST:
+ case OB_DIRECTION_WEST:
+ edge_start = area.y;
+ edge_size = area.height;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ /* do we collide with this window? */
+ if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
+ edge_start, edge_size))
+ return;
+
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ head = RECT_BOTTOM(area);
+ tail = RECT_TOP(area);
+ break;
+ case OB_DIRECTION_SOUTH:
+ head = RECT_TOP(area);
+ tail = RECT_BOTTOM(area);
+ break;
+ case OB_DIRECTION_WEST:
+ head = RECT_RIGHT(area);
+ tail = RECT_LEFT(area);
+ break;
+ case OB_DIRECTION_EAST:
+ head = RECT_LEFT(area);
+ tail = RECT_RIGHT(area);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ case OB_DIRECTION_WEST:
+ /* check if our window is past the head of this window */
+ if (my_head <= head + 1)
+ skip_head = TRUE;
+ /* check if our window's tail is past the tail of this window */
+ if (my_head + my_size - 1 <= tail)
+ skip_tail = TRUE;
+ /* check if the head of this window is closer than the previously
+ chosen edge (take into account that the previously chosen
+ edge might have been a tail, not a head) */
+ if (head + (*near_edge ? 0 : my_size) <= *dest)
+ skip_head = TRUE;
+ /* check if the tail of this window is closer than the previously
+ chosen edge (take into account that the previously chosen
+ edge might have been a head, not a tail) */
+ if (tail - (!*near_edge ? 0 : my_size) <= *dest)
+ skip_tail = TRUE;
+ break;
+ case OB_DIRECTION_SOUTH:
+ case OB_DIRECTION_EAST:
+ /* check if our window is past the head of this window */
+ if (my_head >= head - 1)
+ skip_head = TRUE;
+ /* check if our window's tail is past the tail of this window */
+ if (my_head - my_size + 1 >= tail)
+ skip_tail = TRUE;
+ /* check if the head of this window is closer than the previously
+ chosen edge (take into account that the previously chosen
+ edge might have been a tail, not a head) */
+ if (head - (*near_edge ? 0 : my_size) >= *dest)
+ skip_head = TRUE;
+ /* check if the tail of this window is closer than the previously
+ chosen edge (take into account that the previously chosen
+ edge might have been a head, not a tail) */
+ if (tail + (!*near_edge ? 0 : my_size) >= *dest)
+ skip_tail = TRUE;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ ob_debug("my head %d size %d", my_head, my_size);
+ ob_debug("head %d tail %d dest %d", head, tail, *dest);
+ if (!skip_head) {
+ ob_debug("using near edge %d", head);
+ *dest = head;
+ *near_edge = TRUE;
+ }
+ else if (!skip_tail) {
+ ob_debug("using far edge %d", tail);
+ *dest = tail;
+ *near_edge = FALSE;
+ }
+}
+
+void client_find_edge_directional(ObClient *self, ObDirection dir,
+ gint my_head, gint my_size,
+ gint my_edge_start, gint my_edge_size,
+ gint *dest, gboolean *near_edge)
+{
+ GList *it;
+ Rect *a;
+ Rect dock_area;
+ gint edge;
+ guint i;
+
+ a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
+ &self->frame->area);
+
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ edge = RECT_TOP(*a) - 1;
+ break;
+ case OB_DIRECTION_SOUTH:
+ edge = RECT_BOTTOM(*a) + 1;
+ break;
+ case OB_DIRECTION_EAST:
+ edge = RECT_RIGHT(*a) + 1;
+ break;
+ case OB_DIRECTION_WEST:
+ edge = RECT_LEFT(*a) - 1;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ /* default to the far edge, then narrow it down */
+ *dest = edge;
+ *near_edge = TRUE;
+
+ /* search for edges of monitors */
+ for (i = 0; i < screen_num_monitors; ++i) {
+ Rect *area = screen_area(self->desktop, i, NULL);
+ detect_edge(*area, dir, my_head, my_size, my_edge_start,
+ my_edge_size, dest, near_edge);
+ g_slice_free(Rect, area);
+ }
+
+ /* search for edges of clients */
+ for (it = client_list; it; it = g_list_next(it)) {
+ ObClient *cur = it->data;
+
+ /* skip windows to not bump into */
+ if (cur == self)
+ continue;
+ if (cur->iconic)
+ continue;
+ if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
+ cur->desktop != screen_desktop)
+ continue;
+
+ ob_debug("trying window %s", cur->title);
+
+ detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
+ my_edge_size, dest, near_edge);
+ }
+ dock_get_area(&dock_area);
+ detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
+ my_edge_size, dest, near_edge);
+
+ g_slice_free(Rect, a);
+}
+
+void client_find_move_directional(ObClient *self, ObDirection dir,
+ gint *x, gint *y)
+{
+ gint head, size;
+ gint e, e_start, e_size;
+ gboolean near;
+
+ switch (dir) {
+ case OB_DIRECTION_EAST:
+ head = RECT_RIGHT(self->frame->area);
+ size = self->frame->area.width;
+ e_start = RECT_TOP(self->frame->area);
+ e_size = self->frame->area.height;
+ break;
+ case OB_DIRECTION_WEST:
+ head = RECT_LEFT(self->frame->area);
+ size = self->frame->area.width;
+ e_start = RECT_TOP(self->frame->area);
+ e_size = self->frame->area.height;
+ break;
+ case OB_DIRECTION_NORTH:
+ head = RECT_TOP(self->frame->area);
+ size = self->frame->area.height;
+ e_start = RECT_LEFT(self->frame->area);
+ e_size = self->frame->area.width;
+ break;
+ case OB_DIRECTION_SOUTH:
+ head = RECT_BOTTOM(self->frame->area);
+ size = self->frame->area.height;
+ e_start = RECT_LEFT(self->frame->area);
+ e_size = self->frame->area.width;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ client_find_edge_directional(self, dir, head, size,
+ e_start, e_size, &e, &near);
+ *x = self->frame->area.x;
+ *y = self->frame->area.y;
+ switch (dir) {
+ case OB_DIRECTION_EAST:
+ if (near) e -= self->frame->area.width;
+ else e++;
+ *x = e;
+ break;
+ case OB_DIRECTION_WEST:
+ if (near) e++;
+ else e -= self->frame->area.width;
+ *x = e;
+ break;
+ case OB_DIRECTION_NORTH:
+ if (near) e++;
+ else e -= self->frame->area.height;
+ *y = e;
+ break;
+ case OB_DIRECTION_SOUTH:
+ if (near) e -= self->frame->area.height;
+ else e++;
+ *y = e;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ frame_frame_gravity(self->frame, x, y);
+}
+
+void client_find_resize_directional(ObClient *self, ObDirection side,
+ gboolean grow,
+ gint *x, gint *y, gint *w, gint *h)
+{
+ gint head;
+ gint e, e_start, e_size, delta;
+ gboolean near;
+ ObDirection dir;
+
+ switch (side) {
+ case OB_DIRECTION_EAST:
+ head = RECT_RIGHT(self->frame->area) +
+ (self->size_inc.width - 1) * (grow ? 1 : 0);
+ e_start = RECT_TOP(self->frame->area);
+ e_size = self->frame->area.height;
+ dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
+ break;
+ case OB_DIRECTION_WEST:
+ head = RECT_LEFT(self->frame->area) -
+ (self->size_inc.width - 1) * (grow ? 1 : 0);
+ e_start = RECT_TOP(self->frame->area);
+ e_size = self->frame->area.height;
+ dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
+ break;
+ case OB_DIRECTION_NORTH:
+ head = RECT_TOP(self->frame->area) -
+ (self->size_inc.height - 1) * (grow ? 1 : 0);
+ e_start = RECT_LEFT(self->frame->area);
+ e_size = self->frame->area.width;
+ dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
+ break;
+ case OB_DIRECTION_SOUTH:
+ head = RECT_BOTTOM(self->frame->area) +
+ (self->size_inc.height - 1) * (grow ? 1 : 0);
+ e_start = RECT_LEFT(self->frame->area);
+ e_size = self->frame->area.width;
+ dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ ob_debug("head %d dir %d", head, dir);
+ client_find_edge_directional(self, dir, head, 1,
+ e_start, e_size, &e, &near);
+ ob_debug("edge %d", e);
+ *x = self->frame->area.x;
+ *y = self->frame->area.y;
+ *w = self->frame->area.width;
+ *h = self->frame->area.height;
+ switch (side) {
+ case OB_DIRECTION_EAST:
+ if (grow == near) --e;
+ delta = e - RECT_RIGHT(self->frame->area);
+ *w += delta;
+ break;
+ case OB_DIRECTION_WEST:
+ if (grow == near) ++e;
+ delta = RECT_LEFT(self->frame->area) - e;
+ *x -= delta;
+ *w += delta;
+ break;
+ case OB_DIRECTION_NORTH:
+ if (grow == near) ++e;
+ delta = RECT_TOP(self->frame->area) - e;
+ *y -= delta;
+ *h += delta;
+ break;
+ case OB_DIRECTION_SOUTH:
+ if (grow == near) --e;
+ delta = e - RECT_BOTTOM(self->frame->area);
+ *h += delta;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ frame_frame_gravity(self->frame, x, y);
+ *w -= self->frame->size.left + self->frame->size.right;
+ *h -= self->frame->size.top + self->frame->size.bottom;
+}
+
+ObClient* client_under_pointer(void)
+{
+ gint x, y;
+ GList *it;
+ ObClient *ret = NULL;
+
+ if (screen_pointer_pos(&x, &y)) {
+ for (it = stacking_list; it; it = g_list_next(it)) {
+ if (WINDOW_IS_CLIENT(it->data)) {
+ ObClient *c = WINDOW_AS_CLIENT(it->data);
+ if (c->frame->visible &&
+ /* check the desktop, this is done during desktop
+ switching and windows are shown/hidden status is not
+ reliable */
+ (c->desktop == screen_desktop ||
+ c->desktop == DESKTOP_ALL) &&
+ /* ignore all animating windows */
+ !frame_iconify_animating(c->frame) &&
+ RECT_CONTAINS(c->frame->area, x, y))
+ {
+ ret = c;
+ break;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+gboolean client_has_group_siblings(ObClient *self)
+{
+ return self->group && self->group->members->next;
+}
+
+gboolean client_has_relative(ObClient *self)
+{
+ return client_has_parent(self) ||
+ client_has_group_siblings(self) ||
+ client_has_children(self);
+}
+
+/*! Returns TRUE if the client is running on the same machine as Openbox */
+gboolean client_on_localhost(ObClient *self)
+{
+ return self->client_machine == NULL;
+}
diff --git a/openbox/client.h b/openbox/client.h
new file mode 100644
index 0000000..d5b344f
--- /dev/null
+++ b/openbox/client.h
@@ -0,0 +1,758 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ client.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __client_h
+#define __client_h
+
+#include "misc.h"
+#include "mwm.h"
+#include "geom.h"
+#include "stacking.h"
+#include "window.h"
+#include "obrender/color.h"
+
+#include <glib.h>
+#include <X11/Xlib.h>
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h> /* for pid_t */
+#endif
+
+struct _ObFrame;
+struct _ObGroup;
+struct _ObSessionState;
+struct _ObPrompt;
+
+typedef struct _ObClient ObClient;
+
+/*! Possible window types */
+typedef enum
+{
+ OB_CLIENT_TYPE_DESKTOP, /*!< A desktop (bottom-most window) */
+ OB_CLIENT_TYPE_DOCK, /*!< A dock bar/panel window */
+ OB_CLIENT_TYPE_TOOLBAR, /*!< A toolbar window, pulled off an app */
+ OB_CLIENT_TYPE_MENU, /*!< An unpinned menu from an app */
+ OB_CLIENT_TYPE_UTILITY, /*!< A small utility window such as a palette */
+ OB_CLIENT_TYPE_SPLASH, /*!< A splash screen window */
+ OB_CLIENT_TYPE_DIALOG, /*!< A dialog window */
+ OB_CLIENT_TYPE_NORMAL /*!< A normal application window */
+} ObClientType;
+
+/*! The things the user can do to the client window */
+typedef enum
+{
+ OB_CLIENT_FUNC_RESIZE = 1 << 0, /*!< Allow user resizing */
+ OB_CLIENT_FUNC_MOVE = 1 << 1, /*!< Allow user moving */
+ OB_CLIENT_FUNC_ICONIFY = 1 << 2, /*!< Allow to be iconified */
+ OB_CLIENT_FUNC_MAXIMIZE = 1 << 3, /*!< Allow to be maximized */
+ OB_CLIENT_FUNC_SHADE = 1 << 4, /*!< Allow to be shaded */
+ OB_CLIENT_FUNC_FULLSCREEN = 1 << 5, /*!< Allow to be made fullscreen */
+ OB_CLIENT_FUNC_CLOSE = 1 << 6, /*!< Allow to be closed */
+ OB_CLIENT_FUNC_ABOVE = 1 << 7, /*!< Allow to be put in lower layer */
+ OB_CLIENT_FUNC_BELOW = 1 << 8, /*!< Allow to be put in higher layer */
+ OB_CLIENT_FUNC_UNDECORATE = 1 << 9 /*!< Allow to be undecorated */
+} ObFunctions;
+
+struct _ObClient
+{
+ ObWindow obwin;
+ Window window;
+ gboolean managed;
+
+ /*! If this client is managing an ObPrompt window, then this is set to the
+ prompt */
+ struct _ObPrompt *prompt;
+
+ /*! The window's decorations. NULL while the window is being managed! */
+ struct _ObFrame *frame;
+
+ /*! The number of unmap events to ignore on the window */
+ gint ignore_unmaps;
+
+ /*! The id of the group the window belongs to */
+ struct _ObGroup *group;
+
+ /*! Saved session data to apply to this client */
+ struct _ObSessionState *session;
+
+ /*! Whether or not the client is a transient window. It may or may not
+ have parents when this is true. */
+ gboolean transient;
+ /*! Whether or not the client is transient for its group */
+ gboolean transient_for_group;
+ /*! The client which are parents of this client */
+ GSList *parents;
+ /*! The clients which are transients (children) of this client */
+ GSList *transients;
+ /*! The desktop on which the window resides (0xffffffff for all
+ desktops) */
+ guint desktop;
+
+ /*! The startup id for the startup-notification protocol. This will be
+ NULL if a startup id is not set. */
+ gchar *startup_id;
+
+ /*! Normal window title */
+ gchar *title;
+ /*! Window title when iconified */
+ gchar *icon_title;
+ /*! The title as requested by the client, without any of our own changes */
+ gchar *original_title;
+ /*! Hostname of machine running the client */
+ gchar *client_machine;
+ /*! The command used to run the program. Pre-XSMP window identification. */
+ gchar *wm_command;
+ /*! The PID of the process which owns the window */
+ pid_t pid;
+
+ /*! The application that created the window */
+ gchar *name;
+ /*! The class of the window, can used for grouping */
+ gchar *class;
+ /*! The specified role of the window, used for identification */
+ gchar *role;
+ /*! The session client id for the window. *This can be NULL!* */
+ gchar *sm_client_id;
+
+ /*! The type of window (what its function is) */
+ ObClientType type;
+
+ /*! Position and size of the window
+ This will not always be the actual position of the window on screen, it
+ is, rather, the position requested by the client, to which the window's
+ gravity is applied.
+ */
+ Rect area;
+
+ /*! Position of the client window relative to the root window */
+ Point root_pos;
+
+ /*! Position and size of the window prior to being maximized */
+ Rect pre_max_area;
+ /*! Position and size of the window prior to being fullscreened */
+ Rect pre_fullscreen_area;
+ /*! Remember if the window was maximized before going fullscreen */
+ gboolean pre_fullscreen_max_horz,
+ pre_fullscreen_max_vert;
+
+ /*! The window's strut
+ The strut defines areas of the screen that are marked off-bounds for
+ window placement. In theory, where this window exists.
+ */
+ StrutPartial strut;
+
+ /*! The logical size of the window
+ The "logical" size of the window is refers to the user's perception of
+ the size of the window, and is the value that should be displayed to the
+ user. For example, with xterms, this value it the number of characters
+ being displayed in the terminal, instead of the number of pixels.
+ */
+ Size logical_size;
+
+ /*! Width of the border on the window.
+ The window manager will set this to 0 while the window is being managed,
+ but needs to restore it afterwards, so it is saved here.
+ */
+ gint border_width;
+
+ /*! The minimum aspect ratio the client window can be sized to.
+ A value of 0 means this is ignored.
+ */
+ gfloat min_ratio;
+ /*! The maximum aspect ratio the client window can be sized to.
+ A value of 0 means this is ignored.
+ */
+ gfloat max_ratio;
+
+ /*! The minimum size of the client window
+ If the min is > the max, then the window is not resizable
+ */
+ Size min_size;
+ /*! The maximum size of the client window
+ If the min is > the max, then the window is not resizable
+ */
+ Size max_size;
+ /*! The size of increments to resize the client window by */
+ Size size_inc;
+ /*! The base size of the client window
+ This value should be subtracted from the window's actual size when
+ displaying its size to the user, or working with its min/max size
+ */
+ Size base_size;
+
+ /*! Window decoration and functionality hints */
+ ObMwmHints mwmhints;
+
+ /*! The client's specified colormap */
+ Colormap colormap;
+
+ /*! Where to place the decorated window in relation to the undecorated
+ window */
+ gint gravity;
+
+ /*! The state of the window, one of WithdrawnState, IconicState, or
+ NormalState */
+ glong wmstate;
+
+ /*! True if the client supports the delete_window protocol */
+ gboolean delete_window;
+
+ /*! Was the window's position requested by the application or the user?
+ if by the application, we force it completely onscreen, if by the user
+ we only force it if it tries to go completely offscreen, if neither, we
+ should place the window ourselves when it first appears */
+ guint positioned;
+
+ /*! Was the window's size requested by the application or the user?
+ If by the application we don't let it go outside the available area */
+ guint sized;
+
+ /*! Can the window receive input focus? */
+ gboolean can_focus;
+ /*! Notify the window when it receives focus? */
+ gboolean focus_notify;
+
+ /*! Will the client respond to pings? */
+ gboolean ping;
+ /*! Indicates if the client is trying to close but has stopped responding
+ to pings */
+ gboolean not_responding;
+ /*! A prompt shown when you are trying to close a client that is not
+ responding. It asks if you want to kill the client */
+ struct _ObPrompt *kill_prompt;
+ /*! We tried to close the window with a SIGTERM */
+ gint kill_level;
+
+#ifdef SYNC
+ /*! The client wants to sync during resizes */
+ gboolean sync_request;
+ /*! The XSync counter used for synchronizing during resizes */
+ guint32 sync_counter;
+ /*! The value we're waiting for the counter to reach */
+ gulong sync_counter_value;
+#endif
+
+ /*! The window uses shape extension to be non-rectangular? */
+ gboolean shaped;
+ /*! The window uses shape extension to have non-rectangular input? */
+ gboolean shaped_input;
+
+ /*! The window is modal, so it must be processed before any windows it is
+ related to can be focused */
+ gboolean modal;
+ /*! Only the window's titlebar is displayed */
+ gboolean shaded;
+ /*! The window is iconified */
+ gboolean iconic;
+ /*! The window is maximized to fill the screen vertically */
+ gboolean max_vert;
+ /*! The window is maximized to fill the screen horizontally */
+ gboolean max_horz;
+ /*! The window should not be displayed by pagers */
+ gboolean skip_pager;
+ /*! The window should not be displayed by taskbars */
+ gboolean skip_taskbar;
+ /*! The window is a 'fullscreen' window, and should be on top of all
+ others */
+ gboolean fullscreen;
+ /*! The window should be on top of other windows of the same type.
+ above takes priority over below. */
+ gboolean above;
+ /*! The window should be underneath other windows of the same type.
+ above takes priority over below. */
+ gboolean below;
+ /*! Demands attention flag */
+ gboolean demands_attention;
+
+ /*! The urgent flag */
+ gboolean urgent;
+
+ /*! The layer in which the window will be stacked, windows in lower layers
+ are always below windows in higher layers. */
+ ObStackingLayer layer;
+
+ /*! A bitmask of values in the ObFrameDecorations enum
+ The values in the variable are the decorations that the client wants to
+ be displayed around it.
+ */
+ guint decorations;
+
+ /*! A user option. When this is set to TRUE the client will not ever
+ be decorated.
+ */
+ gboolean undecorated;
+
+ /*! A bitmask of values in the ObFunctions enum
+ The values in the variable specify the ways in which the user is allowed
+ to modify this window.
+ */
+ guint functions;
+
+ /* The window's icon, in a variety of shapes and sizes */
+ RrImage *icon_set;
+
+ /*! Where the window should iconify to/from */
+ Rect icon_geometry;
+
+ /*! A boolean used for algorithms which need to mark clients as visited */
+ gboolean visited;
+};
+
+extern GList *client_list;
+
+void client_startup(gboolean reconfig);
+void client_shutdown(gboolean reconfig);
+
+typedef void (*ObClientCallback)(ObClient *client, gpointer data);
+
+/* Callback functions */
+
+/*! Get notified when the client is unmanaged */
+void client_add_destroy_notify(ObClientCallback func, gpointer data);
+void client_remove_destroy_notify(ObClientCallback func);
+
+/*! Manages a given window
+ @param prompt This specifies an ObPrompt which is being managed. It is
+ possible to manage Openbox-owned windows through this.
+*/
+void client_manage(Window win, struct _ObPrompt *prompt);
+/*! Unmanages all managed windows */
+void client_unmanage_all(void);
+/*! Unmanages a given client */
+void client_unmanage(ObClient *client);
+
+/*! This manages a window only so far as is needed to get it's decorations.
+ This is used when you want to determine a window's decorations before it
+ is mapped. Call client_fake_unmanage() with the returned client when you
+ are done with it. */
+ObClient *client_fake_manage(Window win);
+/*! Free the stuff created by client_fake_manage() */
+void client_fake_unmanage(ObClient *self);
+
+/*! Sets the client list on the root window from the client_list */
+void client_set_list(void);
+
+/*! Determines if the client should be shown or hidden currently.
+ @return TRUE if it should be visible; otherwise, FALSE.
+*/
+gboolean client_should_show(ObClient *self);
+
+/*! Returns if the window should be treated as a normal window.
+ Some windows (desktops, docks, splash screens) have special rules applied
+ to them in a number of places regarding focus or user interaction. */
+gboolean client_normal(ObClient *self);
+
+/*! Returns if the window is one of an application's helper windows
+ (utilty, menu, etc) */
+gboolean client_helper(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
+ that. */
+gboolean client_mouse_focusable(ObClient *self);
+
+/*! Return if the client is a type which should be given focus from the
+ mouse entering the window. This doesn't count for focus cycling, different
+ rules apply to that. */
+gboolean client_enter_focusable(ObClient *self);
+
+/* Returns if the window is focused */
+gboolean client_focused(ObClient *self);
+
+/*! When the client is resized but not moved, figure out the new position
+ for it based on its gravity:
+ http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html#id2512541
+*/
+void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww);
+
+/*! When the client is resized but not moved, figure out the new position
+ for it based on its gravity:
+ http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html#id2512541
+*/
+void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh);
+
+/*! Convert a position/size from a given gravity to the client's true gravity,
+ when the client is only resizing (the reference point doesn't move)
+ */
+void client_convert_gravity_resize(ObClient *self, gint gravity,
+ gint *x, gint *y,
+ gint w, gint h);
+
+#define client_move(self, x, y) \
+ client_configure(self, x, y, self->area.width, self->area.height, TRUE, TRUE,\
+ FALSE)
+#define client_resize(self, w, h) \
+ client_configure(self, self->area.x, self->area.y, w, h, TRUE, TRUE, FALSE)
+#define client_move_resize(self, x, y, w, h) \
+ client_configure(self, x, y, w, h, TRUE, TRUE, FALSE)
+#define client_reconfigure(self, force) \
+ client_configure(self, ((ObClient*)self)->area.x, ((ObClient*)self)->area.y, \
+ ((ObClient*)self)->area.width, \
+ ((ObClient*)self)->area.height, FALSE, TRUE, force)
+
+/*! Figure out where a window will end up and what size it will be if you
+ told it to move/resize to these coordinates.
+
+ These values are what client_configure will give the window.
+
+ @param x The x coordiante of the new position for the client.
+ @param y The y coordiante of the new position for the client.
+ @param w The width component of the new size for the client.
+ @param h The height component of the new size for the client.
+ @param logicalw Returns the width component of the new logical width.
+ This value is only returned when the new w or h calculated
+ differ from the ones passed in.
+ @param logicalh Returns the height component of the new logical height.
+ This value is only returned when the new w or h calculated
+ differ from the ones passed in.
+ @param user Specifies whether this is a user-requested change or a
+ program requested change. For program requested changes, the
+ constraints are not checked.
+*/
+void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
+ gint *logicalw, gint *logicalh,
+ gboolean user);
+
+/*! Move and/or resize the window.
+ This also maintains things like the client's minsize, and size increments.
+ @param x The x coordiante of the new position for the client.
+ @param y The y coordiante of the new position for the client.
+ @param w The width component of the new size for the client.
+ @param h The height component of the new size for the client.
+ @param user Specifies whether this is a user-requested change or a
+ program requested change. For program requested changes, the
+ constraints are not checked.
+ @param final If user is true, then this should specify if this is a final
+ configuration. e.g. Final should be FALSE if doing an
+ interactive move/resize, and then be TRUE for the last call
+ only.
+ @param force_reply Send a ConfigureNotify to the client regardless of if
+ the position/size changed.
+*/
+void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
+ gboolean user, gboolean final, gboolean force_reply);
+
+/*! Finds coordinates to keep a client on the screen.
+ @param self The client
+ @param x The x coord of the client, may be changed.
+ @param y The y coord of the client, may be changed.
+ @param w The width of the client.
+ @param w The height of the client.
+ @param rude Be rude about it. If false, it is only moved if it is entirely
+ not visible. If true, then make sure the window is inside the
+ struts if possible.
+ @return true if the client was moved to be on-screen; false if not.
+*/
+gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
+ gboolean rude);
+
+/*! Moves a client so that it is on screen if it is entirely out of the
+ viewable screen.
+ @param self The client to move
+ @param rude Be rude about it. If false, it is only moved if it is entirely
+ not visible. If true, then make sure the window is inside the
+ struts if possible.
+*/
+void client_move_onscreen(ObClient *self, gboolean rude);
+
+/*! dir is either North, South, East or West. It can't be, for example,
+ Northwest */
+void client_find_edge_directional(ObClient *self, ObDirection dir,
+ gint my_head, gint my_tail,
+ gint my_edge_start, gint my_edge_size,
+ gint *dest, gboolean *near_edge);
+void client_find_move_directional(ObClient *self, ObDirection dir,
+ gint *x, gint *y);
+void client_find_resize_directional(ObClient *self, ObDirection side,
+ gboolean grow,
+ gint *x, gint *y, gint *w, gint *h);
+
+/*! Fullscreen's or unfullscreen's the client window
+ @param fs true if the window should be made fullscreen; false if it should
+ be returned to normal state.
+*/
+void client_fullscreen(ObClient *self, gboolean fs);
+
+/*! 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);
+
+/*! Iconifies or uniconifies the client window
+ @param iconic true if the window should be iconified; false if it should be
+ restored.
+ @param curdesk If iconic is FALSE, then this determines if the window will
+ be uniconified to the current viewable desktop (true) or to
+ its previous desktop (false)
+*/
+void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
+ gboolean hide_animation);
+
+/*! Maximize or unmaximize the client window
+ @param max true if the window should be maximized; false if it should be
+ returned to normal size.
+ @param dir 0 to set both horz and vert, 1 to set horz, 2 to set vert.
+*/
+void client_maximize(ObClient *self, gboolean max, gint dir);
+
+/*! Shades or unshades the client window
+ @param shade true if the window should be shaded; false if it should be
+ unshaded.
+*/
+void client_shade(ObClient *self, gboolean shade);
+
+/*! Set a client window to have decorations or not */
+void client_set_undecorated(ObClient *self, gboolean undecorated);
+
+/*! Hilite the window to make the user notice it */
+void client_hilite(ObClient *self, gboolean hilite);
+
+/*! Request the client to close its window */
+void client_close(ObClient *self);
+
+/*! Kill the client off violently */
+void client_kill(ObClient *self);
+
+/*! Sends the window to the specified desktop
+ @param donthide If TRUE, the window will not be shown/hidden after its
+ desktop has been changed. Generally this should be FALSE.
+ @param dontraise If TRUE, the window will not be raised. Generally this should
+ be FALSE.
+*/
+void client_set_desktop(ObClient *self, guint target, gboolean donthide,
+ gboolean dontraise);
+
+/*! Show the client if it should be shown. Returns if the window is shown. */
+gboolean client_show(ObClient *self);
+
+/*! Show the client if it should be shown. Returns if the window is hidden. */
+gboolean client_hide(ObClient *self);
+
+/*! Show the client if it should be shown, and hide it if it should be
+ hidden. This is for example, when switching desktops.
+*/
+void client_showhide(ObClient *self);
+
+/*! Validate client, by making sure no Destroy or Unmap events exist in
+ the event queue for the window.
+ @return true if the client is valid; false if the client has already
+ been unmapped/destroyed, and so is invalid.
+*/
+gboolean client_validate(ObClient *self);
+
+/*! Sets the wm_state to the specified value */
+void client_set_wm_state(ObClient *self, glong state);
+
+/*! Adjusts the window's net_state
+ This should not be called as part of the window mapping process! It is for
+ use when updating the state post-mapping.<br>
+ client_apply_startup_state is used to do the same things during the mapping
+ process.
+*/
+void client_set_state(ObClient *self, Atom action, glong data1, glong data2);
+
+/* Given a ObClient, find the client that focus would actually be sent to if
+ you wanted to give focus to the specified ObClient. Will return the same
+ ObClient passed to it or another ObClient if appropriate. */
+ObClient *client_focus_target(ObClient *self);
+
+/*! Returns what client_focus would return if passed the same client, but
+ without focusing it or modifying the focus order lists. */
+gboolean client_can_focus(ObClient *self);
+
+/*! Attempt to focus the client window */
+gboolean client_focus(ObClient *self);
+
+/*! Activates the client for use, focusing, uniconifying it, etc. To be used
+ when the user deliberately selects a window for use.
+ @param desktop If true, and the window is on another desktop, it will still
+ be activated.
+ @param here If true, and the window is on another desktop, it will be moved
+ to the current desktop, otherwise the desktop will switch to
+ where the window is.
+ @param raise If true, the client is brought to the front.
+ @param unshade If true, the client is unshaded (if it is shaded)
+ @param user If true, then a user action is what requested the activation;
+ otherwise, it means an application requested it on its own
+*/
+void client_activate(ObClient *self, gboolean desktop, gboolean here,
+ gboolean raise, gboolean unshade, gboolean user);
+
+/*! Bring all of its helper windows to its desktop. These are the utility and
+ stuff windows. */
+void client_bring_helper_windows(ObClient *self);
+
+/*! Bring all of its modal windows to its desktop. */
+void client_bring_modal_windows(ObClient *self);
+
+/*! Calculates the stacking layer for the client window */
+void client_calc_layer(ObClient *self);
+
+/*! Updates the window's transient status, and any parents of it */
+void client_update_transient_for(ObClient *self);
+/*! Update the protocols that the window supports and adjusts things if they
+ change */
+void client_update_protocols(ObClient *self);
+#ifdef SYNC
+/*! Updates the window's sync request counter for resizes */
+void client_update_sync_request_counter(ObClient *self);
+#endif
+/*! Updates the window's colormap */
+void client_update_colormap(ObClient *self, Colormap colormap);
+/*! Updates the requested opacity for the window from the client. */
+void client_update_opacity(ObClient *self);
+/*! Updates the WMNormalHints and adjusts things if they change */
+void client_update_normal_hints(ObClient *self);
+
+/*! Updates the WMHints and adjusts things if they change
+ @param initstate Whether to read the initial_state property from the
+ WMHints. This should only be used during the mapping
+ process.
+*/
+void client_update_wmhints(ObClient *self);
+/*! Updates the window's title and icon title */
+void client_update_title(ObClient *self);
+/*! Updates the strut for the client */
+void client_update_strut(ObClient *self);
+/*! Updates the window's icons */
+void client_update_icons(ObClient *self);
+/*! Updates the window's icon geometry (where to iconify to/from) */
+void client_update_icon_geometry(ObClient *self);
+
+/*! Set up what decor should be shown on the window and what functions should
+ be allowed (ObClient::decorations and ObClient::functions).
+ This also updates the NET_WM_ALLOWED_ACTIONS hint.
+ @param reconfig When TRUE, the window will be reconfigured to show the
+ changes
+*/
+void client_setup_decor_and_functions(ObClient *self, gboolean reconfig);
+
+/*! Sets the window's type and transient flag */
+void client_get_type_and_transientness(ObClient *self);
+/*! Gets the motif wm hints */
+void client_get_mwm_hints(ObClient *self);
+
+/*! Returns a client's icon set, or its parents (recursively) if it doesn't
+ have one
+*/
+RrImage* client_icon(ObClient *self);
+
+/*! Return TRUE if the client is transient for some other window. Return
+ FALSE if it's not transient or there is no window for it to be
+ transient for */
+gboolean client_has_parent(ObClient *self);
+
+/*! Return TRUE if the client has some transient children, and FALSE otherwise.
+*/
+gboolean client_has_children(ObClient *self);
+
+/*! Searches a client's immediate parents for a focused window. The function
+ does not check for the passed client, only for *ONE LEVEL* of its parents.
+ If no focused parent is found, NULL is returned.
+*/
+ObClient *client_search_focus_parent(ObClient *self);
+
+/*! Searches a client's parents for a focused window. The function
+ does not check for the passed client, but searches through all of its
+ parents. If no focused parent is found, NULL is returned.
+*/
+ObClient *client_search_focus_parent_full(ObClient *self);
+
+/*! Searches a client's transients for a focused window. The function does not
+ check for the passed client, only for its transients.
+ If no focused transient is found, NULL is returned.
+*/
+ObClient *client_search_focus_tree(ObClient *self);
+
+/*! Searches a client's transient tree for a focused window. The function
+ searches up the tree and down other branches as well as the passed client's.
+ If no focused client is found, NULL is returned.
+*/
+ObClient *client_search_focus_tree_full(ObClient *self);
+
+/*! Searches a client's group and each member's transients for a focused
+ window. This doesn't go up the window's transient tree at all. If no
+ focused client is found, NULL is returned. */
+ObClient *client_search_focus_group_full(ObClient *self);
+
+/*! Return a modal child of the client window that can be focused.
+ @return A modal child of the client window that can be focused, or 0 if
+ none was found.
+*/
+ObClient *client_search_modal_child(ObClient *self);
+
+/*! Returns a list of top-level windows which this is a transient for.
+ It will only contain more than 1 element if the client is transient for its
+ group.
+*/
+GSList *client_search_all_top_parents(ObClient *self);
+
+/*! Returns a list of top-level windows which this is a transient for, and
+ which are in the same layer as this client.
+ It will only contain more than 1 element if the client is transient for its
+ group.
+*/
+GSList *client_search_all_top_parents_layer(ObClient *self);
+
+/*! Returns the client's parent when it is transient for a direct window
+ rather than a group. If it has no parents, or is transient for the
+ group, this returns null */
+ObClient *client_direct_parent(ObClient *self);
+
+/*! Returns a window's top level parent. This only counts direct parents,
+ not groups if it is transient for its group.
+*/
+ObClient *client_search_top_direct_parent(ObClient *self);
+
+/*! Is one client a direct child of another (i.e. not through the group.)
+ This checks more than one level, so there may be another direct child in
+ between */
+gboolean client_is_direct_child(ObClient *parent, ObClient *child);
+
+/*! Search for a parent of a client. This only searches up *ONE LEVEL*, and
+ returns the searched for parent if it is a parent, or NULL if not. */
+ObClient *client_search_parent(ObClient *self, ObClient *search);
+
+/*! Search for a transient of a client. The transient is returned if it is one,
+ NULL is returned if the given search is not a transient of the client. */
+ObClient *client_search_transient(ObClient *self, ObClient *search);
+
+/*! Set a client window to be above/below other clients.
+ @layer < 0 indicates the client should be placed below other clients.<br />
+ = 0 indicates the client should be placed with other clients.<br />
+ > 0 indicates the client should be placed above other clients.
+*/
+void client_set_layer(ObClient *self, gint layer);
+
+guint client_monitor(ObClient *self);
+
+ObClient* client_under_pointer(void);
+
+gboolean client_has_group_siblings(ObClient *self);
+
+/*! Returns TRUE if the client has a transient child, a parent, or a
+ group member. Returns FALSE otherwise.
+*/
+gboolean client_has_relative(ObClient *self);
+
+/*! Returns TRUE if the client is running on the same machine as Openbox */
+gboolean client_on_localhost(ObClient *self);
+
+#endif
diff --git a/openbox/client_list_combined_menu.c b/openbox/client_list_combined_menu.c
new file mode 100644
index 0000000..c26b6fa
--- /dev/null
+++ b/openbox/client_list_combined_menu.c
@@ -0,0 +1,167 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ client_list_menu.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "openbox.h"
+#include "menu.h"
+#include "menuframe.h"
+#include "screen.h"
+#include "client.h"
+#include "client_list_combined_menu.h"
+#include "focus.h"
+#include "config.h"
+#include "gettext.h"
+
+#include <glib.h>
+
+#define MENU_NAME "client-list-combined-menu"
+
+static ObMenu *combined_menu;
+
+#define SEPARATOR -1
+#define ADD_DESKTOP -2
+#define REMOVE_DESKTOP -3
+
+static void self_cleanup(ObMenu *menu, gpointer data)
+{
+ menu_clear_entries(menu);
+}
+
+static gboolean self_update(ObMenuFrame *frame, gpointer data)
+{
+ ObMenu *menu = frame->menu;
+ ObMenuEntry *e;
+ GList *it;
+ guint desktop;
+
+ menu_clear_entries(menu);
+
+ for (desktop = 0; desktop < screen_num_desktops; desktop++) {
+ gboolean empty = TRUE;
+ gboolean onlyiconic = TRUE;
+
+ menu_add_separator(menu, SEPARATOR, screen_desktop_names[desktop]);
+ for (it = focus_order; it; it = g_list_next(it)) {
+ ObClient *c = it->data;
+ if (focus_valid_target(c, desktop,
+ TRUE, TRUE,
+ FALSE, TRUE, FALSE, FALSE, FALSE))
+ {
+ empty = FALSE;
+
+ if (c->iconic) {
+ gchar *title = g_strdup_printf("(%s)", c->icon_title);
+ e = menu_add_normal(menu, desktop, title, NULL, FALSE);
+ g_free(title);
+ } else {
+ onlyiconic = FALSE;
+ e = menu_add_normal(menu, desktop, c->title, NULL, FALSE);
+ }
+
+ if (config_menu_show_icons) {
+ e->data.normal.icon = client_icon(c);
+ RrImageRef(e->data.normal.icon);
+ e->data.normal.icon_alpha =
+ c->iconic ? OB_ICONIC_ALPHA : 0xff;
+ }
+
+ e->data.normal.data = c;
+ }
+ }
+
+ if (empty || onlyiconic) {
+ /* no entries or only iconified windows, so add a
+ * way to go to this desktop without uniconifying a window */
+ if (!empty)
+ menu_add_separator(menu, SEPARATOR, NULL);
+
+ e = menu_add_normal(menu, desktop, _("Go there..."), NULL, TRUE);
+ if (desktop == screen_desktop)
+ e->data.normal.enabled = FALSE;
+ }
+ }
+
+ if (config_menu_manage_desktops) {
+ menu_add_separator(menu, SEPARATOR, _("Manage desktops"));
+ menu_add_normal(menu, ADD_DESKTOP, _("_Add new desktop"), NULL, TRUE);
+ menu_add_normal(menu, REMOVE_DESKTOP, _("_Remove last desktop"),
+ NULL, TRUE);
+ }
+
+ return TRUE; /* always show the menu */
+}
+
+static void menu_execute(ObMenuEntry *self, ObMenuFrame *f,
+ ObClient *c, guint state, gpointer data)
+{
+ if (self->id == ADD_DESKTOP) {
+ screen_add_desktop(FALSE);
+ menu_frame_hide_all();
+ }
+ else if (self->id == REMOVE_DESKTOP) {
+ screen_remove_desktop(FALSE);
+ menu_frame_hide_all();
+ }
+ else {
+ ObClient *t = self->data.normal.data;
+ if (t) { /* it's set to NULL if its destroyed */
+ gboolean here = state & ShiftMask;
+
+ client_activate(t, TRUE, here, TRUE, TRUE, TRUE);
+ /* if the window is omnipresent then we need to go to its
+ desktop */
+ if (!here && t->desktop == DESKTOP_ALL)
+ screen_set_desktop(self->id, FALSE);
+ }
+ else
+ screen_set_desktop(self->id, TRUE);
+ }
+}
+
+static void client_dest(ObClient *client, gpointer data)
+{
+ /* This concise function removes all references to a closed
+ * client in the client_list_menu, so we don't have to check
+ * in client.c */
+ GList *eit;
+ for (eit = combined_menu->entries; eit; eit = g_list_next(eit)) {
+ ObMenuEntry *meit = eit->data;
+ if (meit->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+ meit->data.normal.data == client)
+ {
+ meit->data.normal.data = NULL;
+ }
+ }
+}
+
+void client_list_combined_menu_startup(gboolean reconfig)
+{
+ if (!reconfig)
+ client_add_destroy_notify(client_dest, NULL);
+
+ combined_menu = menu_new(MENU_NAME, _("Windows"), TRUE, NULL);
+ menu_set_update_func(combined_menu, self_update);
+ menu_set_cleanup_func(combined_menu, self_cleanup);
+ menu_set_execute_func(combined_menu, menu_execute);
+}
+
+void client_list_combined_menu_shutdown(gboolean reconfig)
+{
+ if (!reconfig)
+ client_remove_destroy_notify(client_dest);
+}
diff --git a/openbox/client_list_combined_menu.h b/openbox/client_list_combined_menu.h
new file mode 100644
index 0000000..420e898
--- /dev/null
+++ b/openbox/client_list_combined_menu.h
@@ -0,0 +1,26 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ client_list_menu.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef ob__client_list_combined_menu_h
+#define ob__client_list_combined_menu_h
+
+void client_list_combined_menu_startup(gboolean reconfig);
+void client_list_combined_menu_shutdown(gboolean reconfig);
+
+#endif
diff --git a/openbox/client_list_menu.c b/openbox/client_list_menu.c
new file mode 100644
index 0000000..f3df2a5
--- /dev/null
+++ b/openbox/client_list_menu.c
@@ -0,0 +1,224 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ client_list_menu.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "openbox.h"
+#include "menu.h"
+#include "menuframe.h"
+#include "screen.h"
+#include "client.h"
+#include "client_list_menu.h"
+#include "focus.h"
+#include "config.h"
+#include "gettext.h"
+
+#include <glib.h>
+
+#define MENU_NAME "client-list-menu"
+
+static GSList *desktop_menus;
+
+typedef struct
+{
+ guint desktop;
+} DesktopData;
+
+#define SEPARATOR -1
+#define ADD_DESKTOP -2
+#define REMOVE_DESKTOP -3
+
+static gboolean desk_menu_update(ObMenuFrame *frame, gpointer data)
+{
+ ObMenu *menu = frame->menu;
+ DesktopData *d = data;
+ GList *it;
+ gboolean empty = TRUE;
+ gboolean onlyiconic = TRUE;
+
+ menu_clear_entries(menu);
+
+ for (it = focus_order; it; it = g_list_next(it)) {
+ ObClient *c = it->data;
+ if (focus_valid_target(c, d->desktop,
+ TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE)) {
+ ObMenuEntry *e;
+
+ empty = FALSE;
+
+ if (c->iconic) {
+ gchar *title = g_strdup_printf("(%s)", c->icon_title);
+ e = menu_add_normal(menu, d->desktop, title, NULL, FALSE);
+ g_free(title);
+ } else {
+ onlyiconic = FALSE;
+ e = menu_add_normal(menu, d->desktop, c->title, NULL, FALSE);
+ }
+
+ if (config_menu_show_icons) {
+ e->data.normal.icon = client_icon(c);
+ RrImageRef(e->data.normal.icon);
+ e->data.normal.icon_alpha = c->iconic ? OB_ICONIC_ALPHA : 0xff;
+ }
+
+ e->data.normal.data = c;
+ }
+ }
+
+ if (empty || onlyiconic) {
+ ObMenuEntry *e;
+
+ /* no entries or only iconified windows, so add a
+ * way to go to this desktop without uniconifying a window */
+ if (!empty)
+ menu_add_separator(menu, SEPARATOR, NULL);
+
+ e = menu_add_normal(menu, d->desktop, _("Go there..."), NULL, TRUE);
+ if (d->desktop == screen_desktop)
+ e->data.normal.enabled = FALSE;
+ }
+
+ return TRUE; /* always show */
+}
+
+static void desk_menu_execute(ObMenuEntry *self, ObMenuFrame *f,
+ ObClient *c, guint state, gpointer data)
+{
+ ObClient *t = self->data.normal.data;
+ if (t) { /* it's set to NULL if its destroyed */
+ gboolean here = state & ShiftMask;
+
+ client_activate(t, TRUE, here, TRUE, TRUE, TRUE);
+ /* if the window is omnipresent then we need to go to its
+ desktop */
+ if (!here && t->desktop == DESKTOP_ALL)
+ screen_set_desktop(self->id, FALSE);
+ }
+ else
+ screen_set_desktop(self->id, TRUE);
+}
+
+static void desk_menu_destroy(ObMenu *menu, gpointer data)
+{
+ DesktopData *d = data;
+
+ g_slice_free(DesktopData, d);
+
+ desktop_menus = g_slist_remove(desktop_menus, menu);
+}
+
+static void self_cleanup(ObMenu *menu, gpointer data)
+{
+ menu_clear_entries(menu);
+
+ while (desktop_menus) {
+ menu_free(desktop_menus->data);
+ desktop_menus = g_slist_delete_link(desktop_menus, desktop_menus);
+ }
+}
+
+static gboolean self_update(ObMenuFrame *frame, gpointer data)
+{
+ ObMenu *menu = frame->menu;
+ guint i;
+
+ menu_clear_entries(menu);
+
+ while (desktop_menus) {
+ menu_free(desktop_menus->data);
+ desktop_menus = g_slist_delete_link(desktop_menus, desktop_menus);
+ }
+
+ for (i = 0; i < screen_num_desktops; ++i) {
+ ObMenu *submenu;
+ gchar *name = g_strdup_printf("%s-%u", MENU_NAME, i);
+ DesktopData *ddata = g_slice_new(DesktopData);
+
+ ddata->desktop = i;
+ submenu = menu_new(name, screen_desktop_names[i], FALSE, ddata);
+ menu_set_update_func(submenu, desk_menu_update);
+ menu_set_execute_func(submenu, desk_menu_execute);
+ menu_set_destroy_func(submenu, desk_menu_destroy);
+
+ menu_add_submenu(menu, i, name);
+
+ g_free(name);
+
+ desktop_menus = g_slist_append(desktop_menus, submenu);
+ }
+
+ if (config_menu_manage_desktops) {
+ menu_add_separator(menu, SEPARATOR, NULL);
+ menu_add_normal(menu, ADD_DESKTOP, _("_Add new desktop"), NULL, TRUE);
+ menu_add_normal(menu, REMOVE_DESKTOP, _("_Remove last desktop"),
+ NULL, TRUE);
+ }
+
+ return TRUE; /* always show */
+}
+
+static void self_execute(ObMenuEntry *self, ObMenuFrame *f,
+ ObClient *c, guint state, gpointer data)
+{
+ if (self->id == ADD_DESKTOP) {
+ screen_add_desktop(FALSE);
+ menu_frame_hide_all();
+ }
+ else if (self->id == REMOVE_DESKTOP) {
+ screen_remove_desktop(FALSE);
+ menu_frame_hide_all();
+ }
+}
+
+static void client_dest(ObClient *client, gpointer data)
+{
+ /* This concise function removes all references to a closed
+ * client in the client_list_menu, so we don't have to check
+ * in client.c */
+ GSList *it;
+ for (it = desktop_menus; it; it = g_slist_next(it)) {
+ ObMenu *mit = it->data;
+ GList *eit;
+ for (eit = mit->entries; eit; eit = g_list_next(eit)) {
+ ObMenuEntry *meit = eit->data;
+ if (meit->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+ meit->data.normal.data == client)
+ {
+ meit->data.normal.data = NULL;
+ }
+ }
+ }
+}
+
+void client_list_menu_startup(gboolean reconfig)
+{
+ ObMenu *menu;
+
+ if (!reconfig)
+ client_add_destroy_notify(client_dest, NULL);
+
+ menu = menu_new(MENU_NAME, _("Desktops"), TRUE, NULL);
+ menu_set_update_func(menu, self_update);
+ menu_set_cleanup_func(menu, self_cleanup);
+ menu_set_execute_func(menu, self_execute);
+}
+
+void client_list_menu_shutdown(gboolean reconfig)
+{
+ if (!reconfig)
+ client_remove_destroy_notify(client_dest);
+}
diff --git a/openbox/client_list_menu.h b/openbox/client_list_menu.h
new file mode 100644
index 0000000..860cd50
--- /dev/null
+++ b/openbox/client_list_menu.h
@@ -0,0 +1,26 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ client_list_menu.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef ob__client_list_menu_h
+#define ob__client_list_menu_h
+
+void client_list_menu_startup(gboolean reconfig);
+void client_list_menu_shutdown(gboolean reconfig);
+
+#endif
diff --git a/openbox/client_menu.c b/openbox/client_menu.c
new file mode 100644
index 0000000..c6cdd63
--- /dev/null
+++ b/openbox/client_menu.c
@@ -0,0 +1,421 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ client_menu.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "debug.h"
+#include "menu.h"
+#include "menuframe.h"
+#include "config.h"
+#include "screen.h"
+#include "client.h"
+#include "client_menu.h"
+#include "openbox.h"
+#include "frame.h"
+#include "moveresize.h"
+#include "event.h"
+#include "gettext.h"
+#include "obt/prop.h"
+
+#include <glib.h>
+
+#define CLIENT_MENU_NAME "client-menu"
+#define SEND_TO_MENU_NAME "client-send-to-menu"
+#define LAYER_MENU_NAME "client-layer-menu"
+
+enum {
+ LAYER_TOP = 1,
+ LAYER_NORMAL = 0,
+ LAYER_BOTTOM = -1
+};
+
+enum {
+ CLIENT_SEND_TO,
+ CLIENT_LAYER,
+ CLIENT_ICONIFY,
+ CLIENT_RESTORE,
+ CLIENT_MAXIMIZE,
+ CLIENT_SHADE,
+ CLIENT_DECORATE,
+ CLIENT_MOVE,
+ CLIENT_RESIZE,
+ CLIENT_CLOSE
+};
+
+static void set_icon_color(ObMenuEntry *e)
+{
+ e->data.normal.mask_normal_color = ob_rr_theme->menu_color;
+ e->data.normal.mask_selected_color = ob_rr_theme->menu_selected_color;
+ e->data.normal.mask_disabled_color = ob_rr_theme->menu_disabled_color;
+ e->data.normal.mask_disabled_selected_color =
+ ob_rr_theme->menu_disabled_selected_color;
+}
+
+static gboolean client_menu_update(ObMenuFrame *frame, gpointer data)
+{
+ ObMenu *menu = frame->menu;
+ GList *it;
+
+ if (frame->client == NULL || !client_normal(frame->client))
+ return FALSE; /* don't show the menu */
+
+ for (it = menu->entries; it; it = g_list_next(it)) {
+ ObMenuEntry *e = it->data;
+ gboolean *en = &e->data.normal.enabled; /* save some typing */
+ ObClient *c = frame->client;
+
+ if (e->type == OB_MENU_ENTRY_TYPE_NORMAL) {
+ switch (e->id) {
+ case CLIENT_ICONIFY:
+ *en = c->functions & OB_CLIENT_FUNC_ICONIFY;
+ break;
+ case CLIENT_RESTORE:
+ *en = c->max_horz || c->max_vert;
+ break;
+ case CLIENT_MAXIMIZE:
+ *en = ((c->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
+ (!c->max_horz || !c->max_vert));
+ break;
+ case CLIENT_SHADE:
+ *en = c->functions & OB_CLIENT_FUNC_SHADE;
+ break;
+ case CLIENT_MOVE:
+ *en = c->functions & OB_CLIENT_FUNC_MOVE;
+ break;
+ case CLIENT_RESIZE:
+ *en = c->functions & OB_CLIENT_FUNC_RESIZE;
+ break;
+ case CLIENT_CLOSE:
+ *en = c->functions & OB_CLIENT_FUNC_CLOSE;
+ break;
+ case CLIENT_DECORATE:
+ *en = c->functions & OB_CLIENT_FUNC_UNDECORATE;
+ break;
+ default:
+ *en = TRUE;
+ }
+ }
+ }
+ return TRUE; /* show the menu */
+}
+
+static void client_menu_execute(ObMenuEntry *e, ObMenuFrame *f,
+ ObClient *c, guint state, gpointer data)
+{
+ gint x, y;
+ gulong ignore_start;
+
+ if (!c)
+ return;
+
+ if (!config_focus_under_mouse)
+ ignore_start = event_start_ignore_all_enters();
+
+ switch (e->id) {
+ case CLIENT_ICONIFY:
+ /* the client won't be on screen anymore so hide the menu */
+ menu_frame_hide_all();
+ f = NULL; /* and don't update */
+
+ client_iconify(c, TRUE, FALSE, FALSE);
+ break;
+ case CLIENT_RESTORE:
+ client_maximize(c, FALSE, 0);
+ break;
+ case CLIENT_MAXIMIZE:
+ client_maximize(c, TRUE, 0);
+ break;
+ case CLIENT_SHADE:
+ client_shade(c, !c->shaded);
+ break;
+ case CLIENT_DECORATE:
+ client_set_undecorated(c, !c->undecorated);
+ break;
+ case CLIENT_MOVE:
+ /* this needs to grab the keyboard so hide the menu */
+ menu_frame_hide_all();
+ f = NULL; /* and don't update */
+
+ screen_pointer_pos(&x, &y);
+ moveresize_start(c, x, y, 0,
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD));
+ break;
+ case CLIENT_RESIZE:
+ /* this needs to grab the keyboard so hide the menu */
+ menu_frame_hide_all();
+ f = NULL; /* and don't update */
+
+ screen_pointer_pos(&x, &y);
+ moveresize_start(c, x, y, 0,
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD));
+ break;
+ case CLIENT_CLOSE:
+ client_close(c);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (!config_focus_under_mouse)
+ event_end_ignore_all_enters(ignore_start);
+
+ /* update the menu cuz stuff can have changed */
+ if (f) {
+ client_menu_update(f, NULL);
+ menu_frame_render(f);
+ }
+}
+
+static gboolean layer_menu_update(ObMenuFrame *frame, gpointer data)
+{
+ ObMenu *menu = frame->menu;
+ GList *it;
+
+ if (frame->client == NULL || !client_normal(frame->client))
+ return FALSE; /* don't show the menu */
+
+ for (it = menu->entries; it; it = g_list_next(it)) {
+ ObMenuEntry *e = it->data;
+ gboolean *en = &e->data.normal.enabled; /* save some typing */
+ ObClient *c = frame->client;
+
+ if (e->type == OB_MENU_ENTRY_TYPE_NORMAL) {
+ switch (e->id) {
+ case LAYER_TOP:
+ *en = !c->above && (c->functions & OB_CLIENT_FUNC_ABOVE);
+ break;
+ case LAYER_NORMAL:
+ *en = c->above || c->below;
+ break;
+ case LAYER_BOTTOM:
+ *en = !c->below && (c->functions & OB_CLIENT_FUNC_BELOW);
+ break;
+ default:
+ *en = TRUE;
+ }
+ }
+ }
+ return TRUE; /* show the menu */
+}
+
+static void layer_menu_execute(ObMenuEntry *e, ObMenuFrame *f,
+ ObClient *c, guint state, gpointer data)
+{
+ gulong ignore_start;
+
+ g_assert(c);
+
+ if (!config_focus_under_mouse)
+ ignore_start = event_start_ignore_all_enters();
+
+ client_set_layer(c, e->id);
+
+ if (!config_focus_under_mouse)
+ event_end_ignore_all_enters(ignore_start);
+
+ /* update the menu cuz stuff can have changed */
+ if (f) {
+ layer_menu_update(f, NULL);
+ menu_frame_render(f);
+ }
+}
+
+static gboolean send_to_menu_update(ObMenuFrame *frame, gpointer data)
+{
+ ObMenu *menu = frame->menu;
+ ObClient *c = frame->client;
+ guint i;
+ ObMenuEntry *e;
+ GList *it;
+
+ if (c == NULL || !client_normal(c))
+ return FALSE; /* don't show the menu */
+
+ if (!data)
+ menu_clear_entries(menu);
+
+ if (!menu->entries) {
+ for (i = 0; i <= screen_num_desktops; ++i) {
+ const gchar *name;
+ guint desk;
+
+ if (i == screen_num_desktops) {
+ menu_add_separator(menu, -1, NULL);
+
+ desk = DESKTOP_ALL;
+ name = _("All desktops");
+ } else {
+ desk = i;
+ name = screen_desktop_names[i];
+ }
+
+ e = menu_add_normal(menu, desk, name, NULL, FALSE);
+ e->id = desk;
+ }
+ }
+
+ for (it = menu->entries; it; it = g_list_next(it)) {
+ ObMenuEntry *e = it->data;
+ guint desk = e->id;
+
+ e->data.normal.enabled = c->desktop != desk;
+
+ if ((desk == DESKTOP_ALL && c->desktop != DESKTOP_ALL) ||
+ (c->desktop == DESKTOP_ALL && desk == screen_desktop))
+ {
+ e->data.normal.mask = ob_rr_theme->btn_desk->mask;
+ set_icon_color(e);
+ } else
+ e->data.normal.mask = NULL;
+ }
+
+ return TRUE; /* show the menu */
+}
+
+static void send_to_menu_execute(ObMenuEntry *e, ObMenuFrame *f,
+ ObClient *c, guint state, gpointer data)
+{
+ g_assert(c);
+
+ client_set_desktop(c, e->id, FALSE, FALSE);
+ if (f && c->desktop != screen_desktop && c->desktop != DESKTOP_ALL)
+ /* the client won't even be on the screen anymore, so hide the menu */
+ menu_frame_hide_all();
+ else if (f) {
+ send_to_menu_update(f, (gpointer)1);
+ menu_frame_render(f);
+ }
+}
+
+static void client_menu_place(ObMenuFrame *frame, gint *x, gint *y,
+ gboolean mouse, gpointer data)
+{
+ gint dx, dy;
+
+ if (!mouse && frame->client) {
+ *x = frame->client->frame->area.x;
+
+ /* try below the titlebar */
+ *y = frame->client->frame->area.y + frame->client->frame->size.top -
+ frame->client->frame->bwidth;
+ menu_frame_move_on_screen(frame, *x, *y, &dx, &dy);
+ if (dy != 0) {
+ /* try above the titlebar */
+ *y = frame->client->frame->area.y + frame->client->frame->bwidth -
+ frame->area.height;
+ menu_frame_move_on_screen(frame, *x, *y, &dx, &dy);
+ }
+ if (dy != 0) {
+ /* didnt fit either way, use move on screen's values */
+ *y = frame->client->frame->area.y + frame->client->frame->size.top;
+ menu_frame_move_on_screen(frame, *x, *y, &dx, &dy);
+ }
+
+ *x += dx;
+ *y += dy;
+ } else {
+ gint myx, myy;
+
+ myx = *x;
+ myy = *y;
+
+ /* try to the bottom right of the cursor */
+ menu_frame_move_on_screen(frame, myx, myy, &dx, &dy);
+ if (dx != 0 || dy != 0) {
+ /* try to the bottom left of the cursor */
+ myx = *x - frame->area.width;
+ myy = *y;
+ menu_frame_move_on_screen(frame, myx, myy, &dx, &dy);
+ }
+ if (dx != 0 || dy != 0) {
+ /* try to the top right of the cursor */
+ myx = *x;
+ myy = *y - frame->area.height;
+ menu_frame_move_on_screen(frame, myx, myy, &dx, &dy);
+ }
+ if (dx != 0 || dy != 0) {
+ /* try to the top left of the cursor */
+ myx = *x - frame->area.width;
+ myy = *y - frame->area.height;
+ menu_frame_move_on_screen(frame, myx, myy, &dx, &dy);
+ }
+ if (dx != 0 || dy != 0) {
+ /* if didnt fit on either side so just use what it says */
+ myx = *x;
+ myy = *y;
+ menu_frame_move_on_screen(frame, myx, myy, &dx, &dy);
+ }
+ *x = myx + dx;
+ *y = myy + dy;
+ }
+}
+
+void client_menu_startup(void)
+{
+ ObMenu *menu;
+ ObMenuEntry *e;
+
+ menu = menu_new(LAYER_MENU_NAME, _("_Layer"), TRUE, NULL);
+ menu_show_all_shortcuts(menu, TRUE);
+ menu_set_update_func(menu, layer_menu_update);
+ menu_set_execute_func(menu, layer_menu_execute);
+
+ menu_add_normal(menu, LAYER_TOP, _("Always on _top"), NULL, TRUE);
+ menu_add_normal(menu, LAYER_NORMAL, _("_Normal"), NULL, TRUE);
+ menu_add_normal(menu, LAYER_BOTTOM, _("Always on _bottom"),NULL, TRUE);
+
+ menu = menu_new(SEND_TO_MENU_NAME, _("_Send to desktop"), TRUE, NULL);
+ menu_set_update_func(menu, send_to_menu_update);
+ menu_set_execute_func(menu, send_to_menu_execute);
+
+ menu = menu_new(CLIENT_MENU_NAME, _("Client menu"), TRUE, NULL);
+ menu_show_all_shortcuts(menu, TRUE);
+ menu_set_update_func(menu, client_menu_update);
+ menu_set_place_func(menu, client_menu_place);
+ menu_set_execute_func(menu, client_menu_execute);
+
+ menu_add_submenu(menu, CLIENT_SEND_TO, SEND_TO_MENU_NAME);
+
+ menu_add_submenu(menu, CLIENT_LAYER, LAYER_MENU_NAME);
+
+ e = menu_add_normal(menu, CLIENT_RESTORE, _("R_estore"), NULL, TRUE);
+ e->data.normal.mask = ob_rr_theme->btn_max->toggled_mask;
+ set_icon_color(e);
+
+ menu_add_normal(menu, CLIENT_MOVE, _("_Move"), NULL, TRUE);
+
+ menu_add_normal(menu, CLIENT_RESIZE, _("Resi_ze"), NULL, TRUE);
+
+ e = menu_add_normal(menu, CLIENT_ICONIFY, _("Ico_nify"), NULL, TRUE);
+ e->data.normal.mask = ob_rr_theme->btn_iconify->mask;
+ set_icon_color(e);
+
+ e = menu_add_normal(menu, CLIENT_MAXIMIZE, _("Ma_ximize"), NULL, TRUE);
+ e->data.normal.mask = ob_rr_theme->btn_max->mask;
+ set_icon_color(e);
+
+ e = menu_add_normal(menu, CLIENT_SHADE, _("_Roll up/down"), NULL, TRUE);
+ e->data.normal.mask = ob_rr_theme->btn_shade->mask;
+ set_icon_color(e);
+
+ menu_add_normal(menu, CLIENT_DECORATE, _("Un/_Decorate"), NULL, TRUE);
+
+ menu_add_separator(menu, -1, NULL);
+
+ e = menu_add_normal(menu, CLIENT_CLOSE, _("_Close"), NULL, TRUE);
+ e->data.normal.mask = ob_rr_theme->btn_close->mask;
+ set_icon_color(e);
+}
diff --git a/openbox/client_menu.h b/openbox/client_menu.h
new file mode 100644
index 0000000..5c55516
--- /dev/null
+++ b/openbox/client_menu.h
@@ -0,0 +1,24 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ client_menu.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef ob__client_menu_h
+#define ob__client_menu_h
+
+void client_menu_startup(void);
+
+#endif
diff --git a/openbox/config.c b/openbox/config.c
new file mode 100644
index 0000000..0d9eb68
--- /dev/null
+++ b/openbox/config.c
@@ -0,0 +1,1142 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ config.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "config.h"
+#include "keyboard.h"
+#include "mouse.h"
+#include "actions.h"
+#include "translate.h"
+#include "client.h"
+#include "screen.h"
+#include "openbox.h"
+#include "gettext.h"
+#include "obt/paths.h"
+
+gboolean config_focus_new;
+gboolean config_focus_follow;
+guint config_focus_delay;
+gboolean config_focus_raise;
+gboolean config_focus_last;
+gboolean config_focus_under_mouse;
+gboolean config_unfocus_leave;
+
+ObPlacePolicy config_place_policy;
+gboolean config_place_center;
+ObPlaceMonitor config_place_monitor;
+
+guint config_primary_monitor_index;
+ObPlaceMonitor config_primary_monitor;
+
+StrutPartial config_margins;
+
+gchar *config_theme;
+gboolean config_theme_keepborder;
+guint config_theme_window_list_icon_size;
+
+gchar *config_title_layout;
+
+gboolean config_animate_iconify;
+
+RrFont *config_font_activewindow;
+RrFont *config_font_inactivewindow;
+RrFont *config_font_menuitem;
+RrFont *config_font_menutitle;
+RrFont *config_font_activeosd;
+RrFont *config_font_inactiveosd;
+
+guint config_desktops_num;
+GSList *config_desktops_names;
+guint config_screen_firstdesk;
+guint config_desktop_popup_time;
+
+gboolean config_resize_redraw;
+gint config_resize_popup_show;
+ObResizePopupPos config_resize_popup_pos;
+GravityPoint config_resize_popup_fixed;
+
+ObStackingLayer config_dock_layer;
+gboolean config_dock_floating;
+gboolean config_dock_nostrut;
+ObDirection config_dock_pos;
+gint config_dock_x;
+gint config_dock_y;
+ObOrientation config_dock_orient;
+gboolean config_dock_hide;
+guint config_dock_hide_delay;
+guint config_dock_show_delay;
+guint config_dock_app_move_button;
+guint config_dock_app_move_modifiers;
+
+guint config_keyboard_reset_keycode;
+guint config_keyboard_reset_state;
+
+gint config_mouse_threshold;
+gint config_mouse_dclicktime;
+gint config_mouse_screenedgetime;
+gboolean config_mouse_screenedgewarp;
+
+guint config_menu_hide_delay;
+gboolean config_menu_middle;
+guint config_submenu_show_delay;
+guint config_submenu_hide_delay;
+gboolean config_menu_manage_desktops;
+gboolean config_menu_show_icons;
+
+GSList *config_menu_files;
+
+gint config_resist_win;
+gint config_resist_edge;
+
+GSList *config_per_app_settings;
+
+ObAppSettings* config_create_app_settings(void)
+{
+ ObAppSettings *settings = g_slice_new0(ObAppSettings);
+ settings->type = -1;
+ settings->decor = -1;
+ settings->shade = -1;
+ settings->monitor_type = OB_PLACE_MONITOR_ANY;
+ settings->monitor = -1;
+ settings->focus = -1;
+ settings->desktop = 0;
+ settings->layer = -2;
+ settings->iconic = -1;
+ settings->skip_pager = -1;
+ settings->skip_taskbar = -1;
+ settings->fullscreen = -1;
+ settings->max_horz = -1;
+ settings->max_vert = -1;
+ return settings;
+}
+
+#define copy_if(setting, default) \
+ if (src->setting != default) dst->setting = src->setting
+void config_app_settings_copy_non_defaults(const ObAppSettings *src,
+ ObAppSettings *dst)
+{
+ g_assert(src != NULL);
+ g_assert(dst != NULL);
+
+ copy_if(type, (ObClientType)-1);
+ copy_if(decor, -1);
+ copy_if(shade, -1);
+ copy_if(monitor_type, OB_PLACE_MONITOR_ANY);
+ copy_if(monitor, -1);
+ copy_if(focus, -1);
+ copy_if(desktop, 0);
+ copy_if(layer, -2);
+ copy_if(iconic, -1);
+ copy_if(skip_pager, -1);
+ copy_if(skip_taskbar, -1);
+ copy_if(fullscreen, -1);
+ copy_if(max_horz, -1);
+ copy_if(max_vert, -1);
+
+ if (src->pos_given) {
+ dst->pos_given = TRUE;
+ dst->pos_force = src->pos_force;
+ dst->position = src->position;
+ /* monitor is copied above */
+ }
+}
+
+void config_parse_relative_number(gchar *s, gint *num, gint *denom)
+{
+ *num = strtol(s, &s, 10);
+
+ if (*s == '%') {
+ *denom = 100;
+ } else if (*s == '/') {
+ *denom = atoi(s+1);
+ }
+}
+
+void config_parse_gravity_coord(xmlNodePtr node, GravityCoord *c)
+{
+ gchar *s = obt_xml_node_string(node);
+ if (!g_ascii_strcasecmp(s, "center"))
+ c->center = TRUE;
+ else {
+ gchar *ps = s;
+ if (s[0] == '-')
+ c->opposite = TRUE;
+ if (s[0] == '-' || s[0] == '+')
+ ps++;
+ config_parse_relative_number(ps, &c->pos, &c->denom);
+ }
+ g_free(s);
+}
+
+/*
+ <applications>
+ <application name="aterm">
+ <decor>false</decor>
+ </application>
+ <application name="Rhythmbox">
+ <layer>above</layer>
+ <position>
+ <x>700</x>
+ <y>0</y>
+ <monitor>1</monitor>
+ </position>
+ .. there is a lot more settings available
+ </application>
+ </applications>
+*/
+
+/* 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
+ is on, active: the active monitor, primary: the primary monitor.
+ Layer can be three values, above (Always on top), below
+ (Always on bottom) and everything else (normal behaviour).
+ Positions can be an integer value or center, which will
+ center the window in the specified axis. Position is within
+ the monitor, so <position><x>center</x></position><monitor>2</monitor>
+ will center the window on the second monitor.
+*/
+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;
+
+ while (app) {
+ x_pos_given = FALSE;
+
+ class_set = obt_xml_attr_string(app, "class", &class);
+ name_set = obt_xml_attr_string(app, "name", &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);
+
+ /* validate the type tho */
+ if (type_set) {
+ if (!g_ascii_strcasecmp(type_str, "normal"))
+ type = OB_CLIENT_TYPE_NORMAL;
+ else if (!g_ascii_strcasecmp(type_str, "dialog"))
+ type = OB_CLIENT_TYPE_DIALOG;
+ else if (!g_ascii_strcasecmp(type_str, "splash"))
+ type = OB_CLIENT_TYPE_SPLASH;
+ else if (!g_ascii_strcasecmp(type_str, "utility"))
+ type = OB_CLIENT_TYPE_UTILITY;
+ else if (!g_ascii_strcasecmp(type_str, "menu"))
+ type = OB_CLIENT_TYPE_MENU;
+ else if (!g_ascii_strcasecmp(type_str, "toolbar"))
+ type = OB_CLIENT_TYPE_TOOLBAR;
+ else if (!g_ascii_strcasecmp(type_str, "dock"))
+ type = OB_CLIENT_TYPE_DOCK;
+ else if (!g_ascii_strcasecmp(type_str, "desktop"))
+ type = OB_CLIENT_TYPE_DESKTOP;
+ else
+ 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 ((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);
+ }
+
+ 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");
+ }
+}
+
+/*
+
+<keybind key="C-x">
+ <action name="ChangeDesktop">
+ <desktop>3</desktop>
+ </action>
+</keybind>
+
+*/
+
+static void parse_key(xmlNodePtr node, GList *keylist)
+{
+ gchar *keystring, **keys, **key;
+ xmlNodePtr n;
+ gboolean is_chroot = FALSE;
+
+ if (!obt_xml_attr_string(node, "key", &keystring))
+ return;
+
+ obt_xml_attr_bool(node, "chroot", &is_chroot);
+
+ keys = g_strsplit(keystring, " ", 0);
+ for (key = keys; *key; ++key) {
+ keylist = g_list_append(keylist, *key);
+
+ if ((n = obt_xml_find_node(node->children, "keybind"))) {
+ while (n) {
+ parse_key(n, keylist);
+ n = obt_xml_find_node(n->next, "keybind");
+ }
+ }
+ else if ((n = obt_xml_find_node(node->children, "action"))) {
+ while (n) {
+ ObActionsAct *action;
+
+ action = actions_parse(n);
+ if (action)
+ keyboard_bind(keylist, action);
+ n = obt_xml_find_node(n->next, "action");
+ }
+ }
+
+
+ if (is_chroot)
+ keyboard_chroot(keylist);
+ keylist = g_list_delete_link(keylist, g_list_last(keylist));
+ }
+
+ g_strfreev(keys);
+ g_free(keystring);
+}
+
+static void parse_keyboard(xmlNodePtr node, gpointer d)
+{
+ xmlNodePtr n;
+ gchar *key;
+
+ keyboard_unbind_all();
+
+ if ((n = obt_xml_find_node(node->children, "chainQuitKey"))) {
+ key = obt_xml_node_string(n);
+ translate_key(key, &config_keyboard_reset_state,
+ &config_keyboard_reset_keycode);
+ g_free(key);
+ }
+
+ if ((n = obt_xml_find_node(node->children, "keybind")))
+ while (n) {
+ parse_key(n, NULL);
+ n = obt_xml_find_node(n->next, "keybind");
+ }
+}
+
+/*
+
+<context name="Titlebar">
+ <mousebind button="Left" action="Press">
+ <action name="Raise"></action>
+ </mousebind>
+</context>
+
+*/
+
+static void parse_mouse(xmlNodePtr node, gpointer d)
+{
+ xmlNodePtr n, nbut, nact;
+ gchar *buttonstr;
+ gchar *cxstr;
+ ObMouseAction mact;
+
+ mouse_unbind_all();
+
+ node = node->children;
+
+ if ((n = obt_xml_find_node(node, "dragThreshold")))
+ config_mouse_threshold = obt_xml_node_int(n);
+ if ((n = obt_xml_find_node(node, "doubleClickTime")))
+ config_mouse_dclicktime = obt_xml_node_int(n);
+ if ((n = obt_xml_find_node(node, "screenEdgeWarpTime"))) {
+ config_mouse_screenedgetime = obt_xml_node_int(n);
+ /* minimum value of 25 for this property, when it is 1 and you hit the
+ edge it basically never stops */
+ if (config_mouse_screenedgetime && config_mouse_screenedgetime < 25)
+ config_mouse_screenedgetime = 25;
+ }
+ if ((n = obt_xml_find_node(node, "screenEdgeWarpMouse")))
+ config_mouse_screenedgewarp = obt_xml_node_bool(n);
+
+ n = obt_xml_find_node(node, "context");
+ while (n) {
+ gchar *modcxstr;
+ ObFrameContext cx;
+
+ if (!obt_xml_attr_string(n, "name", &cxstr))
+ goto next_n;
+
+ modcxstr = g_strdup(cxstr); /* make a copy to mutilate */
+ while (frame_next_context_from_string(modcxstr, &cx)) {
+ if (!cx) {
+ gchar *s = strchr(modcxstr, ' ');
+ if (s) {
+ *s = '\0';
+ g_message(_("Invalid context \"%s\" in mouse binding"),
+ modcxstr);
+ *s = ' ';
+ }
+ continue;
+ }
+
+ nbut = obt_xml_find_node(n->children, "mousebind");
+ while (nbut) {
+ if (!obt_xml_attr_string(nbut, "button", &buttonstr))
+ goto next_nbut;
+ if (obt_xml_attr_contains(nbut, "action", "press"))
+ mact = OB_MOUSE_ACTION_PRESS;
+ else if (obt_xml_attr_contains(nbut, "action", "release"))
+ mact = OB_MOUSE_ACTION_RELEASE;
+ else if (obt_xml_attr_contains(nbut, "action", "click"))
+ mact = OB_MOUSE_ACTION_CLICK;
+ else if (obt_xml_attr_contains(nbut, "action","doubleclick"))
+ mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
+ else if (obt_xml_attr_contains(nbut, "action", "drag"))
+ mact = OB_MOUSE_ACTION_MOTION;
+ else
+ goto next_nbut;
+
+ nact = obt_xml_find_node(nbut->children, "action");
+ while (nact) {
+ ObActionsAct *action;
+
+ if ((action = actions_parse(nact)))
+ mouse_bind(buttonstr, cx, mact, action);
+ nact = obt_xml_find_node(nact->next, "action");
+ }
+ g_free(buttonstr);
+ next_nbut:
+ nbut = obt_xml_find_node(nbut->next, "mousebind");
+ }
+ }
+ g_free(modcxstr);
+ g_free(cxstr);
+ next_n:
+ n = obt_xml_find_node(n->next, "context");
+ }
+}
+
+static void parse_focus(xmlNodePtr node, gpointer d)
+{
+ xmlNodePtr n;
+
+ node = node->children;
+
+ if ((n = obt_xml_find_node(node, "focusNew")))
+ config_focus_new = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "followMouse")))
+ config_focus_follow = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "focusDelay")))
+ config_focus_delay = obt_xml_node_int(n);
+ if ((n = obt_xml_find_node(node, "raiseOnFocus")))
+ config_focus_raise = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "focusLast")))
+ config_focus_last = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "underMouse")))
+ config_focus_under_mouse = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "unfocusOnLeave")))
+ config_unfocus_leave = obt_xml_node_bool(n);
+}
+
+static void parse_placement(xmlNodePtr node, gpointer d)
+{
+ xmlNodePtr n;
+
+ node = node->children;
+
+ 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;
+ else if (obt_xml_node_contains(n, "mouse"))
+ config_place_monitor = OB_PLACE_MONITOR_MOUSE;
+ else if (obt_xml_node_contains(n, "any"))
+ config_place_monitor = OB_PLACE_MONITOR_ANY;
+ }
+ if ((n = obt_xml_find_node(node, "primaryMonitor"))) {
+ config_primary_monitor_index = obt_xml_node_int(n);
+ if (!config_primary_monitor_index) {
+ if (obt_xml_node_contains(n, "mouse"))
+ config_primary_monitor = OB_PLACE_MONITOR_MOUSE;
+ }
+ }
+}
+
+static void parse_margins(xmlNodePtr node, gpointer d)
+{
+ xmlNodePtr n;
+
+ node = node->children;
+
+ if ((n = obt_xml_find_node(node, "top")))
+ config_margins.top = MAX(0, obt_xml_node_int(n));
+ if ((n = obt_xml_find_node(node, "left")))
+ config_margins.left = MAX(0, obt_xml_node_int(n));
+ if ((n = obt_xml_find_node(node, "right")))
+ config_margins.right = MAX(0, obt_xml_node_int(n));
+ if ((n = obt_xml_find_node(node, "bottom")))
+ config_margins.bottom = MAX(0, obt_xml_node_int(n));
+}
+
+static void parse_theme(xmlNodePtr node, gpointer d)
+{
+ xmlNodePtr n;
+
+ node = node->children;
+
+ if ((n = obt_xml_find_node(node, "name"))) {
+ gchar *c;
+
+ g_free(config_theme);
+ c = obt_xml_node_string(n);
+ config_theme = obt_paths_expand_tilde(c);
+ g_free(c);
+ }
+ if ((n = obt_xml_find_node(node, "titleLayout"))) {
+ gchar *c, *d;
+
+ g_free(config_title_layout);
+ config_title_layout = obt_xml_node_string(n);
+
+ /* replace duplicates with spaces */
+ for (c = config_title_layout; *c != '\0'; ++c)
+ for (d = c+1; *d != '\0'; ++d)
+ if (*c == *d) *d = ' ';
+ }
+ if ((n = obt_xml_find_node(node, "keepBorder")))
+ config_theme_keepborder = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "animateIconify")))
+ config_animate_iconify = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "windowListIconSize"))) {
+ config_theme_window_list_icon_size = obt_xml_node_int(n);
+ if (config_theme_window_list_icon_size < 16)
+ config_theme_window_list_icon_size = 16;
+ else if (config_theme_window_list_icon_size > 96)
+ config_theme_window_list_icon_size = 96;
+ }
+
+ n = obt_xml_find_node(node, "font");
+ while (n) {
+ xmlNodePtr fnode;
+ RrFont **font;
+ gchar *name = g_strdup(RrDefaultFontFamily);
+ gint size = RrDefaultFontSize;
+ RrFontWeight weight = RrDefaultFontWeight;
+ RrFontSlant slant = RrDefaultFontSlant;
+
+ if (obt_xml_attr_contains(n, "place", "ActiveWindow"))
+ font = &config_font_activewindow;
+ else if (obt_xml_attr_contains(n, "place", "InactiveWindow"))
+ font = &config_font_inactivewindow;
+ else if (obt_xml_attr_contains(n, "place", "MenuHeader"))
+ font = &config_font_menutitle;
+ else if (obt_xml_attr_contains(n, "place", "MenuItem"))
+ font = &config_font_menuitem;
+ else if (obt_xml_attr_contains(n, "place", "ActiveOnScreenDisplay"))
+ font = &config_font_activeosd;
+ else if (obt_xml_attr_contains(n, "place", "OnScreenDisplay"))
+ font = &config_font_activeosd;
+ else if (obt_xml_attr_contains(n, "place","InactiveOnScreenDisplay"))
+ font = &config_font_inactiveosd;
+ else
+ goto next_font;
+
+ if ((fnode = obt_xml_find_node(n->children, "name"))) {
+ g_free(name);
+ name = obt_xml_node_string(fnode);
+ }
+ if ((fnode = obt_xml_find_node(n->children, "size"))) {
+ int s = obt_xml_node_int(fnode);
+ if (s > 0) size = s;
+ }
+ if ((fnode = obt_xml_find_node(n->children, "weight"))) {
+ gchar *w = obt_xml_node_string(fnode);
+ if (!g_ascii_strcasecmp(w, "Bold"))
+ weight = RR_FONTWEIGHT_BOLD;
+ g_free(w);
+ }
+ if ((fnode = obt_xml_find_node(n->children, "slant"))) {
+ gchar *s = obt_xml_node_string(fnode);
+ if (!g_ascii_strcasecmp(s, "Italic"))
+ slant = RR_FONTSLANT_ITALIC;
+ if (!g_ascii_strcasecmp(s, "Oblique"))
+ slant = RR_FONTSLANT_OBLIQUE;
+ g_free(s);
+ }
+
+ *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
+ g_free(name);
+ next_font:
+ n = obt_xml_find_node(n->next, "font");
+ }
+}
+
+static void parse_desktops(xmlNodePtr node, gpointer d)
+{
+ xmlNodePtr n;
+
+ node = node->children;
+
+ if ((n = obt_xml_find_node(node, "number"))) {
+ gint d = obt_xml_node_int(n);
+ if (d > 0)
+ config_desktops_num = (unsigned) d;
+ }
+ if ((n = obt_xml_find_node(node, "firstdesk"))) {
+ gint d = obt_xml_node_int(n);
+ if (d > 0)
+ config_screen_firstdesk = (unsigned) d;
+ }
+ if ((n = obt_xml_find_node(node, "names"))) {
+ GSList *it;
+ xmlNodePtr nname;
+
+ for (it = config_desktops_names; it; it = it->next)
+ g_free(it->data);
+ g_slist_free(config_desktops_names);
+ config_desktops_names = NULL;
+
+ nname = obt_xml_find_node(n->children, "name");
+ while (nname) {
+ config_desktops_names =
+ g_slist_append(config_desktops_names,
+ obt_xml_node_string(nname));
+ nname = obt_xml_find_node(nname->next, "name");
+ }
+ }
+ if ((n = obt_xml_find_node(node, "popupTime")))
+ config_desktop_popup_time = obt_xml_node_int(n);
+}
+
+static void parse_resize(xmlNodePtr node, gpointer d)
+{
+ xmlNodePtr n;
+
+ node = node->children;
+
+ if ((n = obt_xml_find_node(node, "drawContents")))
+ config_resize_redraw = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "popupShow"))) {
+ config_resize_popup_show = obt_xml_node_int(n);
+ if (obt_xml_node_contains(n, "Always"))
+ config_resize_popup_show = 2;
+ else if (obt_xml_node_contains(n, "Never"))
+ config_resize_popup_show = 0;
+ else if (obt_xml_node_contains(n, "Nonpixel"))
+ config_resize_popup_show = 1;
+ }
+ if ((n = obt_xml_find_node(node, "popupPosition"))) {
+ if (obt_xml_node_contains(n, "Top"))
+ config_resize_popup_pos = OB_RESIZE_POS_TOP;
+ else if (obt_xml_node_contains(n, "Center"))
+ config_resize_popup_pos = OB_RESIZE_POS_CENTER;
+ else if (obt_xml_node_contains(n, "Fixed")) {
+ config_resize_popup_pos = OB_RESIZE_POS_FIXED;
+
+ if ((n = obt_xml_find_node(node, "popupFixedPosition"))) {
+ xmlNodePtr n2;
+
+ if ((n2 = obt_xml_find_node(n->children, "x")))
+ config_parse_gravity_coord(n2,
+ &config_resize_popup_fixed.x);
+ if ((n2 = obt_xml_find_node(n->children, "y")))
+ config_parse_gravity_coord(n2,
+ &config_resize_popup_fixed.y);
+
+ config_resize_popup_fixed.x.pos =
+ MAX(config_resize_popup_fixed.x.pos, 0);
+ config_resize_popup_fixed.y.pos =
+ MAX(config_resize_popup_fixed.y.pos, 0);
+ }
+ }
+ }
+}
+
+static void parse_dock(xmlNodePtr node, gpointer d)
+{
+ xmlNodePtr n;
+
+ node = node->children;
+
+ if ((n = obt_xml_find_node(node, "position"))) {
+ if (obt_xml_node_contains(n, "TopLeft"))
+ config_dock_floating = FALSE,
+ config_dock_pos = OB_DIRECTION_NORTHWEST;
+ else if (obt_xml_node_contains(n, "Top"))
+ config_dock_floating = FALSE,
+ config_dock_pos = OB_DIRECTION_NORTH;
+ else if (obt_xml_node_contains(n, "TopRight"))
+ config_dock_floating = FALSE,
+ config_dock_pos = OB_DIRECTION_NORTHEAST;
+ else if (obt_xml_node_contains(n, "Right"))
+ config_dock_floating = FALSE,
+ config_dock_pos = OB_DIRECTION_EAST;
+ else if (obt_xml_node_contains(n, "BottomRight"))
+ config_dock_floating = FALSE,
+ config_dock_pos = OB_DIRECTION_SOUTHEAST;
+ else if (obt_xml_node_contains(n, "Bottom"))
+ config_dock_floating = FALSE,
+ config_dock_pos = OB_DIRECTION_SOUTH;
+ else if (obt_xml_node_contains(n, "BottomLeft"))
+ config_dock_floating = FALSE,
+ config_dock_pos = OB_DIRECTION_SOUTHWEST;
+ else if (obt_xml_node_contains(n, "Left"))
+ config_dock_floating = FALSE,
+ config_dock_pos = OB_DIRECTION_WEST;
+ else if (obt_xml_node_contains(n, "Floating"))
+ config_dock_floating = TRUE;
+ }
+ if (config_dock_floating) {
+ if ((n = obt_xml_find_node(node, "floatingX")))
+ config_dock_x = obt_xml_node_int(n);
+ if ((n = obt_xml_find_node(node, "floatingY")))
+ config_dock_y = obt_xml_node_int(n);
+ } else {
+ if ((n = obt_xml_find_node(node, "noStrut")))
+ config_dock_nostrut = obt_xml_node_bool(n);
+ }
+ if ((n = obt_xml_find_node(node, "stacking"))) {
+ if (obt_xml_node_contains(n, "normal"))
+ config_dock_layer = OB_STACKING_LAYER_NORMAL;
+ else if (obt_xml_node_contains(n, "below"))
+ config_dock_layer = OB_STACKING_LAYER_BELOW;
+ else if (obt_xml_node_contains(n, "above"))
+ config_dock_layer = OB_STACKING_LAYER_ABOVE;
+ }
+ if ((n = obt_xml_find_node(node, "direction"))) {
+ if (obt_xml_node_contains(n, "horizontal"))
+ config_dock_orient = OB_ORIENTATION_HORZ;
+ else if (obt_xml_node_contains(n, "vertical"))
+ config_dock_orient = OB_ORIENTATION_VERT;
+ }
+ if ((n = obt_xml_find_node(node, "autoHide")))
+ config_dock_hide = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "hideDelay")))
+ config_dock_hide_delay = obt_xml_node_int(n);
+ if ((n = obt_xml_find_node(node, "showDelay")))
+ config_dock_show_delay = obt_xml_node_int(n);
+ if ((n = obt_xml_find_node(node, "moveButton"))) {
+ gchar *str = obt_xml_node_string(n);
+ guint b, s;
+ if (translate_button(str, &s, &b)) {
+ config_dock_app_move_button = b;
+ config_dock_app_move_modifiers = s;
+ } else {
+ g_message(_("Invalid button \"%s\" specified in config file"), str);
+ }
+ g_free(str);
+ }
+}
+
+static void parse_menu(xmlNodePtr node, gpointer d)
+{
+ xmlNodePtr n;
+ node = node->children;
+
+ if ((n = obt_xml_find_node(node, "hideDelay")))
+ config_menu_hide_delay = obt_xml_node_int(n);
+ if ((n = obt_xml_find_node(node, "middle")))
+ config_menu_middle = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "submenuShowDelay")))
+ config_submenu_show_delay = obt_xml_node_int(n);
+ if ((n = obt_xml_find_node(node, "submenuHideDelay")))
+ config_submenu_hide_delay = obt_xml_node_int(n);
+ if ((n = obt_xml_find_node(node, "manageDesktops")))
+ config_menu_manage_desktops = obt_xml_node_bool(n);
+ if ((n = obt_xml_find_node(node, "showIcons"))) {
+ config_menu_show_icons = obt_xml_node_bool(n);
+#ifndef USE_IMLIB2
+ if (config_menu_show_icons)
+ g_message(_("Openbox was compiled without Imlib2 image loading support. Icons in menus will not be loaded."));
+#endif
+ }
+
+ while ((node = obt_xml_find_node(node, "file"))) {
+ gchar *c = obt_xml_node_string(node);
+ config_menu_files = g_slist_append(config_menu_files,
+ obt_paths_expand_tilde(c));
+ g_free(c);
+ node = node->next;
+ }
+}
+
+static void parse_resistance(xmlNodePtr node, gpointer d)
+{
+ xmlNodePtr n;
+
+ node = node->children;
+ if ((n = obt_xml_find_node(node, "strength")))
+ config_resist_win = obt_xml_node_int(n);
+ if ((n = obt_xml_find_node(node, "screen_edge_strength")))
+ config_resist_edge = obt_xml_node_int(n);
+}
+
+typedef struct
+{
+ const gchar *key;
+ const gchar *actname;
+} ObDefKeyBind;
+
+static void bind_default_keyboard(void)
+{
+ ObDefKeyBind *it;
+ ObDefKeyBind binds[] = {
+ { "A-Tab", "NextWindow" },
+ { "S-A-Tab", "PreviousWindow" },
+ { "A-F4", "Close" },
+ { NULL, NULL }
+ };
+ for (it = binds; it->key; ++it) {
+ GList *l = g_list_append(NULL, g_strdup(it->key));
+ keyboard_bind(l, actions_parse_string(it->actname));
+ }
+}
+
+typedef struct
+{
+ const gchar *button;
+ const gchar *context;
+ const ObMouseAction mact;
+ const gchar *actname;
+} ObDefMouseBind;
+
+static void bind_default_mouse(void)
+{
+ ObDefMouseBind *it;
+ ObDefMouseBind binds[] = {
+ { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
+ { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
+ { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
+ { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
+ { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
+ { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
+ { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
+ { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
+ { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
+ { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
+ { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
+ { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
+ { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
+ { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
+ { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
+ { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximize" },
+ { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
+ { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
+ { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
+ { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
+ { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
+ { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
+ { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
+ { "Left", "Top", OB_MOUSE_ACTION_MOTION, "Resize" },
+ { "Left", "Bottom", OB_MOUSE_ACTION_MOTION, "Resize" },
+ { "Left", "Left", OB_MOUSE_ACTION_MOTION, "Resize" },
+ { "Left", "Right", OB_MOUSE_ACTION_MOTION, "Resize" },
+ { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
+ { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
+ { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
+ { NULL, NULL, 0, NULL }
+ };
+
+ for (it = binds; it->button; ++it)
+ mouse_bind(it->button, frame_context_from_string(it->context),
+ it->mact, actions_parse_string(it->actname));
+}
+
+void config_startup(ObtXmlInst *i)
+{
+ config_focus_new = TRUE;
+ config_focus_follow = FALSE;
+ config_focus_delay = 0;
+ config_focus_raise = FALSE;
+ config_focus_last = TRUE;
+ config_focus_under_mouse = FALSE;
+ config_unfocus_leave = FALSE;
+
+ 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;
+ config_primary_monitor = OB_PLACE_MONITOR_ACTIVE;
+
+ obt_xml_register(i, "placement", parse_placement, NULL);
+
+ STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ obt_xml_register(i, "margins", parse_margins, NULL);
+
+ config_theme = NULL;
+
+ config_animate_iconify = TRUE;
+ config_title_layout = g_strdup("NLIMC");
+ config_theme_keepborder = TRUE;
+ config_theme_window_list_icon_size = 36;
+
+ config_font_activewindow = NULL;
+ config_font_inactivewindow = NULL;
+ config_font_menuitem = NULL;
+ config_font_menutitle = NULL;
+ config_font_activeosd = NULL;
+ config_font_inactiveosd = NULL;
+
+ obt_xml_register(i, "theme", parse_theme, NULL);
+
+ config_desktops_num = 4;
+ config_screen_firstdesk = 1;
+ config_desktops_names = NULL;
+ config_desktop_popup_time = 875;
+
+ obt_xml_register(i, "desktops", parse_desktops, NULL);
+
+ config_resize_redraw = TRUE;
+ config_resize_popup_show = 1; /* nonpixel increments */
+ config_resize_popup_pos = OB_RESIZE_POS_CENTER;
+ GRAVITY_COORD_SET(config_resize_popup_fixed.x, 0, FALSE, FALSE);
+ GRAVITY_COORD_SET(config_resize_popup_fixed.y, 0, FALSE, FALSE);
+
+ obt_xml_register(i, "resize", parse_resize, NULL);
+
+ config_dock_layer = OB_STACKING_LAYER_ABOVE;
+ config_dock_pos = OB_DIRECTION_NORTHEAST;
+ config_dock_floating = FALSE;
+ config_dock_nostrut = FALSE;
+ config_dock_x = 0;
+ config_dock_y = 0;
+ config_dock_orient = OB_ORIENTATION_VERT;
+ config_dock_hide = FALSE;
+ config_dock_hide_delay = 300;
+ config_dock_show_delay = 300;
+ config_dock_app_move_button = 2; /* middle */
+ config_dock_app_move_modifiers = 0;
+
+ obt_xml_register(i, "dock", parse_dock, NULL);
+
+ translate_key("C-g", &config_keyboard_reset_state,
+ &config_keyboard_reset_keycode);
+
+ bind_default_keyboard();
+
+ obt_xml_register(i, "keyboard", parse_keyboard, NULL);
+
+ config_mouse_threshold = 8;
+ config_mouse_dclicktime = 500;
+ config_mouse_screenedgetime = 400;
+ config_mouse_screenedgewarp = FALSE;
+
+ bind_default_mouse();
+
+ obt_xml_register(i, "mouse", parse_mouse, NULL);
+
+ config_resist_win = 10;
+ config_resist_edge = 20;
+
+ obt_xml_register(i, "resistance", parse_resistance, NULL);
+
+ config_menu_hide_delay = 250;
+ config_menu_middle = FALSE;
+ config_submenu_show_delay = 100;
+ config_submenu_hide_delay = 400;
+ config_menu_manage_desktops = TRUE;
+ config_menu_files = NULL;
+ config_menu_show_icons = TRUE;
+
+ obt_xml_register(i, "menu", parse_menu, NULL);
+
+ config_per_app_settings = NULL;
+
+ obt_xml_register(i, "applications", parse_per_app_settings, NULL);
+}
+
+void config_shutdown(void)
+{
+ GSList *it;
+
+ g_free(config_theme);
+
+ g_free(config_title_layout);
+
+ RrFontClose(config_font_activewindow);
+ RrFontClose(config_font_inactivewindow);
+ RrFontClose(config_font_menuitem);
+ RrFontClose(config_font_menutitle);
+ RrFontClose(config_font_activeosd);
+ RrFontClose(config_font_inactiveosd);
+
+ for (it = config_desktops_names; it; it = g_slist_next(it))
+ g_free(it->data);
+ g_slist_free(config_desktops_names);
+
+ for (it = config_menu_files; it; it = g_slist_next(it))
+ g_free(it->data);
+ g_slist_free(config_menu_files);
+
+ 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->title) g_pattern_spec_free(itd->title);
+ if (itd->class) g_pattern_spec_free(itd->class);
+ g_slice_free(ObAppSettings, it->data);
+ }
+ g_slist_free(config_per_app_settings);
+}
diff --git a/openbox/config.h b/openbox/config.h
new file mode 100644
index 0000000..730dc39
--- /dev/null
+++ b/openbox/config.h
@@ -0,0 +1,225 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ config.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __config_h
+#define __config_h
+
+#include "misc.h"
+#include "stacking.h"
+#include "place.h"
+#include "client.h"
+#include "geom.h"
+#include "moveresize.h"
+#include "obrender/render.h"
+#include "obt/xml.h"
+
+#include <glib.h>
+
+typedef struct _ObAppSettings ObAppSettings;
+
+struct _ObAppSettings
+{
+ GPatternSpec *class;
+ GPatternSpec *name;
+ GPatternSpec *role;
+ GPatternSpec *title;
+ ObClientType type;
+
+ GravityPoint position;
+ gboolean pos_given;
+ gboolean pos_force;
+
+ guint desktop;
+ gint shade;
+ gint decor;
+ gint focus;
+ ObPlaceMonitor monitor_type;
+ gint monitor;
+ gint iconic;
+ gint skip_pager;
+ gint skip_taskbar;
+ gint max_horz;
+ gint max_vert;
+ gint fullscreen;
+
+ gint layer;
+};
+
+/*! Should new windows be focused */
+extern gboolean config_focus_new;
+/*! Focus windows when the mouse enters them */
+extern gboolean config_focus_follow;
+/*! Timeout for focusing windows on focus follows mouse, in milliseconds */
+extern guint config_focus_delay;
+/*! If windows should automatically be raised when they are focused in
+ focus follows mouse */
+extern gboolean config_focus_raise;
+/*! Focus the last focused window, not under the mouse, in follow mouse mode */
+extern gboolean config_focus_last;
+/*! Try keep focus on the window under the mouse when the mouse is not moving
+ */
+extern gboolean config_focus_under_mouse;
+/*! Remove focus from windows when the mouse leaves them
+ */
+extern gboolean config_unfocus_leave;
+
+/*! 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;
+
+/*! Place dialogs and stuff on this monitor. Index starts at 1. If this is
+ 0, then use the config_primary_monitor instead. */
+extern guint config_primary_monitor_index;
+/*! Where to place dialogs and stuff if it is not specified by index. */
+extern ObPlaceMonitor config_primary_monitor;
+
+/*! User-specified margins around the edge of the screen(s) */
+extern StrutPartial config_margins;
+
+/*! When true windows' contents are refreshed while they are resized; otherwise
+ they are not updated until the resize is complete */
+extern gboolean config_resize_redraw;
+/*! show move/resize popups? 0 = no, 1 = always, 2 = only
+ resizing !1 increments */
+extern gint config_resize_popup_show;
+/*! where to show the resize popup */
+extern ObResizePopupPos config_resize_popup_pos;
+/*! where to place the popup if it's in a fixed position */
+extern GravityPoint config_resize_popup_fixed;
+
+/*! The stacking layer the dock will reside in */
+extern ObStackingLayer config_dock_layer;
+/*! Is the dock floating */
+extern gboolean config_dock_floating;
+/*! Don't use a strut for the dock */
+extern gboolean config_dock_nostrut;
+/*! Where to place the dock if not floating */
+extern ObDirection config_dock_pos;
+/*! If config_dock_floating, this is the top-left corner's
+ position */
+extern gint config_dock_x;
+/*! If config_dock_floating, this is the top-left corner's
+ position */
+extern gint config_dock_y;
+/*! Whether the dock places the dockapps in it horizontally or vertically */
+extern ObOrientation config_dock_orient;
+/*! Whether to auto-hide the dock when the pointer is not over it */
+extern gboolean config_dock_hide;
+/*! The number of milliseconds to wait before hiding the dock */
+extern guint config_dock_hide_delay;
+/*! The number of milliseconds to wait before showing the dock */
+extern guint config_dock_show_delay;
+/*! The mouse button to be used to move dock apps */
+extern guint config_dock_app_move_button;
+/*! The modifiers to be used with the button to move dock apps */
+extern guint config_dock_app_move_modifiers;
+
+/*! The name of the theme */
+extern gchar *config_theme;
+
+/*! Show the one-pixel border after toggleDecor */
+extern gboolean config_theme_keepborder;
+/*! Titlebar button layout */
+extern gchar *config_title_layout;
+/*! Animate windows iconifying and restoring */
+extern gboolean config_animate_iconify;
+/*! Size of icons in focus switching dialogs */
+extern guint config_theme_window_list_icon_size;
+
+/*! The font for the active window's title */
+extern RrFont *config_font_activewindow;
+/*! The font for inactive windows' titles */
+extern RrFont *config_font_inactivewindow;
+/*! The font for menu titles */
+extern RrFont *config_font_menutitle;
+/*! The font for menu items */
+extern RrFont *config_font_menuitem;
+/*! The font for on-screen-displays/popups' active text */
+extern RrFont *config_font_activeosd;
+/*! The font for on-screen-displays/popups' inactive text */
+extern RrFont *config_font_inactiveosd;
+
+/*! The number of desktops */
+extern guint config_desktops_num;
+/*! Desktop to start on, put 5 to start in the center of a 3x3 grid */
+extern guint config_screen_firstdesk;
+/*! Names for the desktops */
+extern GSList *config_desktops_names;
+/*! Amount of time to show the desktop switch dialog */
+extern guint config_desktop_popup_time;
+
+/*! The keycode of the key combo which resets the keybaord chains */
+extern guint config_keyboard_reset_keycode;
+/*! The modifiers of the key combo which resets the keybaord chains */
+extern guint config_keyboard_reset_state;
+
+/*! Number of pixels a drag must go before being considered a drag */
+extern gint config_mouse_threshold;
+/*! Number of milliseconds within which 2 clicks must occur to be a
+ double-click */
+extern gint config_mouse_dclicktime;
+/*! Number of milliseconds that the mouse has to be on the screen edge before
+ a screen edge event is triggered */
+extern gint config_mouse_screenedgetime;
+/*! When TRUE, the mouse is warped to the other side of the desktop after
+ switching desktops from bumping the screen edge */
+extern gboolean config_mouse_screenedgewarp;
+
+/*! Number of pixels to resist while crossing another window's edge */
+extern gint config_resist_win;
+/*! Number of pixels to resist while crossing a screen's edge */
+extern gint config_resist_edge;
+
+/*! Delay for hiding menu when opening in milliseconds */
+extern guint config_menu_hide_delay;
+/*! Center menus vertically about the parent entry */
+extern gboolean config_menu_middle;
+/*! Delay before opening a submenu in milliseconds */
+extern guint config_submenu_show_delay;
+/*! Delay before closing a submenu in milliseconds */
+extern guint config_submenu_hide_delay;
+/*! Show manage desktops in client_list_menu */
+extern gboolean config_menu_manage_desktops;
+/*! Load & show icons in user-defined menus */
+extern gboolean config_menu_show_icons;
+/*! User-specified menu files */
+extern GSList *config_menu_files;
+/*! Per app settings */
+extern GSList *config_per_app_settings;
+
+void config_startup(ObtXmlInst *i);
+void config_shutdown(void);
+
+/*! Create an ObAppSettings structure with the default values */
+ObAppSettings* config_create_app_settings(void);
+/*! Copies any settings in src to dest, if they are their default value in
+ src. */
+void config_app_settings_copy_non_defaults(const ObAppSettings *src,
+ ObAppSettings *dest);
+/*! Parses an x geometry style position, with some extensions like ratios
+ and percentages */
+void config_parse_gravity_coord(xmlNodePtr node, GravityCoord *c);
+/*! Parses a rational number or percentage into num and denom */
+void config_parse_relative_number(gchar *s, gint *num, gint *denom);
+
+#endif
diff --git a/openbox/debug.c b/openbox/debug.c
new file mode 100644
index 0000000..ad083b1
--- /dev/null
+++ b/openbox/debug.c
@@ -0,0 +1,201 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ debug.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "debug.h"
+#include "prompt.h"
+#include "openbox.h"
+#include "gettext.h"
+#include "obt/paths.h"
+
+#include <glib.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+static gboolean enabled_types[OB_DEBUG_TYPE_NUM] = {FALSE};
+static FILE *log_file = NULL;
+static guint rr_handler_id = 0;
+static guint obt_handler_id = 0;
+static guint ob_handler_id = 0;
+static guint ob_handler_prompt_id = 0;
+static GList *prompt_queue = NULL;
+static gboolean allow_prompts = TRUE;
+
+static void log_handler(const gchar *log_domain, GLogLevelFlags log_level,
+ const gchar *message, gpointer user_data);
+static void prompt_handler(const gchar *log_domain, GLogLevelFlags log_level,
+ const gchar *message, gpointer user_data);
+
+void ob_debug_startup(void)
+{
+ ObtPaths *p = obt_paths_new();
+ gchar *dir = g_build_filename(obt_paths_cache_home(p),
+ "openbox", NULL);
+
+ /* log messages to a log file! fancy, no? */
+ if (!obt_paths_mkdir_path(dir, 0777))
+ g_message(_("Unable to make directory '%s': %s"),
+ dir, g_strerror(errno));
+ else {
+ gchar *name = g_build_filename(obt_paths_cache_home(p),
+ "openbox", "openbox.log", NULL);
+ /* unlink it before opening to remove competition */
+ unlink(name);
+ log_file = fopen(name, "w");
+ g_free(name);
+ }
+
+ rr_handler_id =
+ g_log_set_handler("ObRender", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL |
+ G_LOG_FLAG_RECURSION, log_handler, NULL);
+ obt_handler_id =
+ g_log_set_handler("Obt", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL |
+ G_LOG_FLAG_RECURSION, log_handler, NULL);
+ ob_handler_id =
+ g_log_set_handler("Openbox", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL |
+ G_LOG_FLAG_RECURSION, log_handler, NULL);
+ ob_handler_prompt_id =
+ g_log_set_handler("Openbox", G_LOG_LEVEL_MASK & ~G_LOG_LEVEL_DEBUG,
+ prompt_handler, NULL);
+
+ obt_paths_unref(p);
+ g_free(dir);
+}
+
+void ob_debug_shutdown(void)
+{
+ g_log_remove_handler("ObRender", rr_handler_id);
+ g_log_remove_handler("Obt", obt_handler_id);
+ g_log_remove_handler("Openbox", ob_handler_id);
+ g_log_remove_handler("Openbox", ob_handler_prompt_id);
+
+ if (log_file) {
+ fclose(log_file);
+ log_file = NULL;
+ }
+}
+
+void ob_debug_enable(ObDebugType type, gboolean enable)
+{
+ g_assert(type < OB_DEBUG_TYPE_NUM);
+ enabled_types[type] = enable;
+}
+
+static inline void log_print(FILE *out, const gchar* log_domain,
+ const gchar *level, const gchar *message)
+{
+ fprintf(out, "%s", log_domain);
+ fprintf(out, "-");
+ fprintf(out, "%s", level);
+ fprintf(out, ": ");
+ fprintf(out, "%s", message);
+ fprintf(out, "\n");
+ fflush(out);
+}
+
+static void log_handler(const gchar *log_domain, GLogLevelFlags log_level,
+ const gchar *message, gpointer data)
+{
+ FILE *out;
+ const gchar *level;
+
+ switch (log_level & G_LOG_LEVEL_MASK) {
+ case G_LOG_LEVEL_DEBUG: level = "Debug"; out = stdout; break;
+ case G_LOG_LEVEL_INFO: level = "Info"; out = stdout; break;
+ case G_LOG_LEVEL_MESSAGE: level = "Message"; out = stdout; break;
+ case G_LOG_LEVEL_WARNING: level = "Warning"; out = stderr; break;
+ case G_LOG_LEVEL_CRITICAL: level = "Critical"; out = stderr; break;
+ case G_LOG_LEVEL_ERROR: level = "Error"; out = stderr; break;
+ default: g_assert_not_reached(); /* invalid level.. */
+ }
+
+ log_print(out, log_domain, level, message);
+ if (log_file) log_print(log_file, log_domain, level, message);
+}
+
+static void prompt_handler(const gchar *log_domain, GLogLevelFlags log_level,
+ const gchar *message, gpointer data)
+{
+ if (ob_state() == OB_STATE_RUNNING && allow_prompts)
+ prompt_queue = g_list_prepend(prompt_queue, g_strdup(message));
+ else
+ log_handler(log_domain, log_level, message, data);
+}
+
+static inline void log_argv(ObDebugType type,
+ const gchar *format, va_list args)
+{
+ const gchar *prefix;
+ gchar *message;
+
+ g_assert(type < OB_DEBUG_TYPE_NUM);
+ if (!enabled_types[type]) return;
+
+ switch (type) {
+ case OB_DEBUG_FOCUS: prefix = "(FOCUS) "; break;
+ case OB_DEBUG_APP_BUGS: prefix = "(APPLICATION BUG) "; break;
+ case OB_DEBUG_SM: prefix = "(SESSION) "; break;
+ default: prefix = NULL; break;
+ }
+
+ message = g_strdup_vprintf(format, args);
+ if (prefix) {
+ gchar *a = message;
+ message = g_strconcat(prefix, message, NULL);
+ g_free(a);
+ }
+
+ g_debug("%s", message);
+ g_free(message);
+}
+
+void ob_debug(const gchar *a, ...)
+{
+ va_list vl;
+
+ va_start(vl, a);
+ log_argv(OB_DEBUG_NORMAL, a, vl);
+ va_end(vl);
+}
+
+void ob_debug_type(ObDebugType type, const gchar *a, ...)
+{
+ va_list vl;
+
+ va_start(vl, a);
+ log_argv(type, a, vl);
+ va_end(vl);
+}
+
+void ob_debug_show_prompts(void)
+{
+ if (prompt_queue) {
+ allow_prompts = FALSE; /* avoid recursive prompts */
+ while (prompt_queue) {
+ prompt_show_message(prompt_queue->data, "Openbox", _("Close"));
+ g_free(prompt_queue->data);
+ prompt_queue = g_list_delete_link(prompt_queue, prompt_queue);
+ }
+ allow_prompts = TRUE;
+ }
+}
diff --git a/openbox/debug.h b/openbox/debug.h
new file mode 100644
index 0000000..13c5598
--- /dev/null
+++ b/openbox/debug.h
@@ -0,0 +1,43 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ debug.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __ob__debug_h
+#define __ob__debug_h
+
+#include <glib.h>
+
+void ob_debug_startup(void);
+void ob_debug_shutdown(void);
+
+void ob_debug(const gchar *a, ...);
+
+typedef enum {
+ OB_DEBUG_NORMAL,
+ OB_DEBUG_FOCUS,
+ OB_DEBUG_APP_BUGS,
+ OB_DEBUG_SM,
+ OB_DEBUG_TYPE_NUM
+} ObDebugType;
+
+void ob_debug_type(ObDebugType type, const gchar *a, ...);
+
+void ob_debug_enable(ObDebugType type, gboolean enable);
+
+void ob_debug_show_prompts(void);
+
+#endif
diff --git a/openbox/dock.c b/openbox/dock.c
new file mode 100644
index 0000000..c26eee6
--- /dev/null
+++ b/openbox/dock.c
@@ -0,0 +1,693 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ dock.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "debug.h"
+#include "dock.h"
+#include "screen.h"
+#include "config.h"
+#include "grab.h"
+#include "openbox.h"
+#include "obrender/theme.h"
+#include "obt/prop.h"
+
+#define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
+ EnterWindowMask | LeaveWindowMask)
+#define DOCKAPP_EVENT_MASK (StructureNotifyMask)
+#define DOCK_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
+ ButtonMotionMask)
+
+static ObDock *dock;
+static guint show_timeout_id;
+static guint hide_timeout_id;
+
+StrutPartial dock_strut;
+
+static void dock_app_grab_button(ObDockApp *app, gboolean grab)
+{
+ if (grab) {
+ grab_button_full(config_dock_app_move_button,
+ config_dock_app_move_modifiers, app->icon_win,
+ ButtonPressMask | ButtonReleaseMask |
+ ButtonMotionMask,
+ GrabModeAsync, OB_CURSOR_MOVE);
+ } else {
+ ungrab_button(config_dock_app_move_button,
+ config_dock_app_move_modifiers, app->icon_win);
+ }
+}
+
+static guint window_hash(Window *w) { return *w; }
+static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; }
+
+void dock_startup(gboolean reconfig)
+{
+ XSetWindowAttributes attrib;
+
+ if (reconfig) {
+ GList *it;
+
+ XSetWindowBorder(obt_display, dock->frame,
+ RrColorPixel(ob_rr_theme->osd_border_color));
+ XSetWindowBorderWidth(obt_display, dock->frame, ob_rr_theme->obwidth);
+
+ RrAppearanceFree(dock->a_frame);
+ dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_bg);
+
+ stacking_add(DOCK_AS_WINDOW(dock));
+
+ dock_configure();
+ dock_hide(TRUE);
+
+ for (it = dock->dock_apps; it; it = g_list_next(it))
+ dock_app_grab_button(it->data, TRUE);
+ return;
+ }
+
+ STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+
+ dock = g_slice_new0(ObDock);
+ dock->obwin.type = OB_WINDOW_CLASS_DOCK;
+
+ dock->hidden = TRUE;
+
+ dock->dock_map = g_hash_table_new((GHashFunc)window_hash,
+ (GEqualFunc)window_comp);
+
+ attrib.event_mask = DOCK_EVENT_MASK;
+ attrib.override_redirect = True;
+ attrib.do_not_propagate_mask = DOCK_NOPROPAGATEMASK;
+ dock->frame = XCreateWindow(obt_display, obt_root(ob_screen),
+ 0, 0, 1, 1, 0,
+ RrDepth(ob_rr_inst), InputOutput,
+ RrVisual(ob_rr_inst),
+ CWOverrideRedirect | CWEventMask |
+ CWDontPropagate,
+ &attrib);
+ dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_bg);
+ XSetWindowBorder(obt_display, dock->frame,
+ RrColorPixel(ob_rr_theme->osd_border_color));
+ XSetWindowBorderWidth(obt_display, dock->frame, ob_rr_theme->obwidth);
+
+ /* Setting the window type so xcompmgr can tell what it is */
+ OBT_PROP_SET32(dock->frame, NET_WM_WINDOW_TYPE, ATOM,
+ OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK));
+
+ window_add(&dock->frame, DOCK_AS_WINDOW(dock));
+ stacking_add(DOCK_AS_WINDOW(dock));
+}
+
+void dock_shutdown(gboolean reconfig)
+{
+ if (reconfig) {
+ GList *it;
+
+ stacking_remove(DOCK_AS_WINDOW(dock));
+
+ for (it = dock->dock_apps; it; it = g_list_next(it))
+ dock_app_grab_button(it->data, FALSE);
+ return;
+ }
+
+ g_hash_table_destroy(dock->dock_map);
+
+ XDestroyWindow(obt_display, dock->frame);
+ RrAppearanceFree(dock->a_frame);
+ window_remove(dock->frame);
+ stacking_remove(dock);
+ g_slice_free(ObDock, dock);
+ dock = NULL;
+}
+
+void dock_manage(Window icon_win, Window name_win)
+{
+ ObDockApp *app;
+ XWindowAttributes attrib;
+ gchar **data;
+
+ app = g_slice_new0(ObDockApp);
+ app->name_win = name_win;
+ app->icon_win = icon_win;
+
+ if (OBT_PROP_GETSS_TYPE(app->name_win, WM_CLASS, STRING_NO_CC, &data)) {
+ if (data[0]) {
+ app->name = g_strdup(data[0]);
+ if (data[1])
+ app->class = g_strdup(data[1]);
+ }
+ g_strfreev(data);
+ }
+
+ if (app->name == NULL) app->name = g_strdup("");
+ if (app->class == NULL) app->class = g_strdup("");
+
+ if (XGetWindowAttributes(obt_display, app->icon_win, &attrib)) {
+ app->w = attrib.width;
+ app->h = attrib.height;
+ } else {
+ app->w = app->h = 64;
+ }
+
+ dock->dock_apps = g_list_append(dock->dock_apps, app);
+ g_hash_table_insert(dock->dock_map, &app->icon_win, app);
+ dock_configure();
+
+ XReparentWindow(obt_display, app->icon_win, dock->frame, app->x, app->y);
+ /*
+ This is the same case as in frame.c for client windows. When Openbox is
+ starting, the window is already mapped so we see unmap events occur for
+ it. There are 2 unmap events generated that we see, one with the 'event'
+ member set the root window, and one set to the client, but both get
+ handled and need to be ignored.
+ */
+ if (ob_state() == OB_STATE_STARTING)
+ app->ignore_unmaps += 2;
+ XChangeSaveSet(obt_display, app->icon_win, SetModeInsert);
+ XMapWindow(obt_display, app->icon_win);
+
+ if (app->name_win != app->icon_win) {
+ XReparentWindow(obt_display, app->name_win, dock->frame, -1000, -1000);
+ XChangeSaveSet(obt_display, app->name_win, SetModeInsert);
+ XMapWindow(obt_display, app->name_win);
+ }
+
+ XSync(obt_display, False);
+
+ XSelectInput(obt_display, app->icon_win, DOCKAPP_EVENT_MASK);
+
+ dock_app_grab_button(app, TRUE);
+
+ ob_debug("Managed Dock App: 0x%lx 0x%lx (%s)",
+ app->icon_win, app->name_win, app->class);
+
+ grab_server(FALSE);
+}
+
+void dock_unmanage_all(void)
+{
+ while (dock->dock_apps)
+ dock_unmanage(dock->dock_apps->data, TRUE);
+}
+
+void dock_unmanage(ObDockApp *app, gboolean reparent)
+{
+ dock_app_grab_button(app, FALSE);
+ XSelectInput(obt_display, app->icon_win, NoEventMask);
+ /* remove the window from our save set */
+ XChangeSaveSet(obt_display, app->icon_win, SetModeDelete);
+ XSync(obt_display, False);
+
+ if (reparent) {
+ XReparentWindow(obt_display, app->icon_win, obt_root(ob_screen), 0, 0);
+ if (app->name_win != app->icon_win)
+ XReparentWindow(obt_display, app->name_win,
+ obt_root(ob_screen), 0, 0);
+ }
+
+ dock->dock_apps = g_list_remove(dock->dock_apps, app);
+ g_hash_table_remove(dock->dock_map, &app->icon_win);
+ dock_configure();
+
+ ob_debug("Unmanaged Dock App: 0x%lx (%s)", app->icon_win, app->class);
+
+ g_free(app->name);
+ g_free(app->class);
+ g_slice_free(ObDockApp, app);
+}
+
+void dock_configure(void)
+{
+ GList *it;
+ gint hspot, vspot;
+ gint gravity;
+ gint l, r, t, b;
+ gint strw, strh;
+ const Rect *a;
+ gint hidesize;
+
+ RrMargins(dock->a_frame, &l, &t, &r, &b);
+ hidesize = MAX(1, ob_rr_theme->obwidth);
+
+ dock->area.width = dock->area.height = 0;
+
+ /* get the size */
+ for (it = dock->dock_apps; it; it = g_list_next(it)) {
+ ObDockApp *app = it->data;
+ switch (config_dock_orient) {
+ case OB_ORIENTATION_HORZ:
+ dock->area.width += app->w;
+ dock->area.height = MAX(dock->area.height, app->h);
+ break;
+ case OB_ORIENTATION_VERT:
+ dock->area.width = MAX(dock->area.width, app->w);
+ dock->area.height += app->h;
+ break;
+ }
+ }
+
+ if (dock->dock_apps) {
+ dock->area.width += l + r;
+ dock->area.height += t + b;
+ }
+
+ hspot = l;
+ vspot = t;
+
+ /* position the apps */
+ for (it = dock->dock_apps; it; it = g_list_next(it)) {
+ ObDockApp *app = it->data;
+ switch (config_dock_orient) {
+ case OB_ORIENTATION_HORZ:
+ app->x = hspot;
+ app->y = (dock->area.height - app->h) / 2;
+ hspot += app->w;
+ break;
+ case OB_ORIENTATION_VERT:
+ app->x = (dock->area.width - app->w) / 2;
+ app->y = vspot;
+ vspot += app->h;
+ break;
+ }
+
+ XMoveWindow(obt_display, app->icon_win, app->x, app->y);
+ }
+
+ /* used for calculating offsets */
+ dock->area.width += ob_rr_theme->obwidth * 2;
+ dock->area.height += ob_rr_theme->obwidth * 2;
+
+ a = screen_physical_area_all_monitors();
+
+ /* calculate position */
+ if (config_dock_floating) {
+ dock->area.x = config_dock_x;
+ dock->area.y = config_dock_y;
+ gravity = NorthWestGravity;
+ } else {
+ switch (config_dock_pos) {
+ case OB_DIRECTION_NORTHWEST:
+ dock->area.x = 0;
+ dock->area.y = 0;
+ gravity = NorthWestGravity;
+ break;
+ case OB_DIRECTION_NORTH:
+ dock->area.x = a->width / 2;
+ dock->area.y = 0;
+ gravity = NorthGravity;
+ break;
+ case OB_DIRECTION_NORTHEAST:
+ dock->area.x = a->width;
+ dock->area.y = 0;
+ gravity = NorthEastGravity;
+ break;
+ case OB_DIRECTION_WEST:
+ dock->area.x = 0;
+ dock->area.y = a->height / 2;
+ gravity = WestGravity;
+ break;
+ case OB_DIRECTION_EAST:
+ dock->area.x = a->width;
+ dock->area.y = a->height / 2;
+ gravity = EastGravity;
+ break;
+ case OB_DIRECTION_SOUTHWEST:
+ dock->area.x = 0;
+ dock->area.y = a->height;
+ gravity = SouthWestGravity;
+ break;
+ case OB_DIRECTION_SOUTH:
+ dock->area.x = a->width / 2;
+ dock->area.y = a->height;
+ gravity = SouthGravity;
+ break;
+ case OB_DIRECTION_SOUTHEAST:
+ dock->area.x = a->width;
+ dock->area.y = a->height;
+ gravity = SouthEastGravity;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ switch(gravity) {
+ case NorthGravity:
+ case CenterGravity:
+ case SouthGravity:
+ dock->area.x -= dock->area.width / 2;
+ break;
+ case NorthEastGravity:
+ case EastGravity:
+ case SouthEastGravity:
+ dock->area.x -= dock->area.width;
+ break;
+ }
+ switch(gravity) {
+ case WestGravity:
+ case CenterGravity:
+ case EastGravity:
+ dock->area.y -= dock->area.height / 2;
+ break;
+ case SouthWestGravity:
+ case SouthGravity:
+ case SouthEastGravity:
+ dock->area.y -= dock->area.height;
+ break;
+ }
+
+ if (config_dock_hide && dock->hidden) {
+ if (!config_dock_floating) {
+ switch (config_dock_pos) {
+ case OB_DIRECTION_NORTHWEST:
+ switch (config_dock_orient) {
+ case OB_ORIENTATION_HORZ:
+ dock->area.y -= dock->area.height - hidesize;
+ break;
+ case OB_ORIENTATION_VERT:
+ dock->area.x -= dock->area.width - hidesize;
+ break;
+ }
+ break;
+ case OB_DIRECTION_NORTH:
+ dock->area.y -= dock->area.height - hidesize;
+ break;
+ case OB_DIRECTION_NORTHEAST:
+ switch (config_dock_orient) {
+ case OB_ORIENTATION_HORZ:
+ dock->area.y -= dock->area.height - hidesize;
+ break;
+ case OB_ORIENTATION_VERT:
+ dock->area.x += dock->area.width - hidesize;
+ break;
+ }
+ break;
+ case OB_DIRECTION_WEST:
+ dock->area.x -= dock->area.width - hidesize;
+ break;
+ case OB_DIRECTION_EAST:
+ dock->area.x += dock->area.width - hidesize;
+ break;
+ case OB_DIRECTION_SOUTHWEST:
+ switch (config_dock_orient) {
+ case OB_ORIENTATION_HORZ:
+ dock->area.y += dock->area.height - hidesize;
+ break;
+ case OB_ORIENTATION_VERT:
+ dock->area.x -= dock->area.width - hidesize;
+ break;
+ } break;
+ case OB_DIRECTION_SOUTH:
+ dock->area.y += dock->area.height - hidesize;
+ break;
+ case OB_DIRECTION_SOUTHEAST:
+ switch (config_dock_orient) {
+ case OB_ORIENTATION_HORZ:
+ dock->area.y += dock->area.height - hidesize;
+ break;
+ case OB_ORIENTATION_VERT:
+ dock->area.x += dock->area.width - hidesize;
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ if (!config_dock_floating && config_dock_hide) {
+ strw = hidesize;
+ strh = hidesize;
+ } else {
+ strw = dock->area.width;
+ strh = dock->area.height;
+ }
+
+ /* set the strut */
+ if (!dock->dock_apps) {
+ STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+ }
+ else if (config_dock_floating || config_dock_nostrut) {
+ STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+ }
+ else {
+ switch (config_dock_pos) {
+ case OB_DIRECTION_NORTHWEST:
+ switch (config_dock_orient) {
+ case OB_ORIENTATION_HORZ:
+ STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
+ 0, 0, dock->area.x, dock->area.x
+ + dock->area.width - 1, 0, 0, 0, 0);
+ break;
+ case OB_ORIENTATION_VERT:
+ STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
+ dock->area.y, dock->area.y
+ + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
+ break;
+ }
+ break;
+ case OB_DIRECTION_NORTH:
+ STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
+ 0, 0, dock->area.x, dock->area.x
+ + dock->area.width - 1, 0, 0, 0, 0);
+ break;
+ case OB_DIRECTION_NORTHEAST:
+ switch (config_dock_orient) {
+ case OB_ORIENTATION_HORZ:
+ STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
+ 0, 0, dock->area.x, dock->area.x
+ + dock->area.width -1, 0, 0, 0, 0);
+ break;
+ case OB_ORIENTATION_VERT:
+ STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
+ 0, 0, 0, 0, dock->area.y, dock->area.y
+ + dock->area.height - 1, 0, 0);
+ break;
+ }
+ break;
+ case OB_DIRECTION_WEST:
+ STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
+ dock->area.y, dock->area.y
+ + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
+ break;
+ case OB_DIRECTION_EAST:
+ STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
+ 0, 0, 0, 0, dock->area.y, dock->area.y
+ + dock->area.height - 1, 0, 0);
+ break;
+ case OB_DIRECTION_SOUTHWEST:
+ switch (config_dock_orient) {
+ case OB_ORIENTATION_HORZ:
+ STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
+ 0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
+ + dock->area.width - 1);
+ break;
+ case OB_ORIENTATION_VERT:
+ STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
+ dock->area.y, dock->area.y
+ + dock->area.height - 1, 0, 0, 0, 0, 0, 0);
+ break;
+ }
+ break;
+ case OB_DIRECTION_SOUTH:
+ STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
+ 0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x
+ + dock->area.width - 1);
+ break;
+ case OB_DIRECTION_SOUTHEAST:
+ switch (config_dock_orient) {
+ case OB_ORIENTATION_HORZ:
+ STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
+ 0, 0, 0, 0, 0, 0, dock->area.x,
+ dock->area.x + dock->area.width - 1);
+ break;
+ case OB_ORIENTATION_VERT:
+ STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
+ 0, 0, 0, 0, dock->area.y, dock->area.y
+ + dock->area.height - 1, 0, 0);
+ break;
+ }
+ break;
+ }
+ }
+
+ /* not used for actually sizing shit */
+ dock->area.width -= ob_rr_theme->obwidth * 2;
+ dock->area.height -= ob_rr_theme->obwidth * 2;
+
+ if (dock->dock_apps) {
+ g_assert(dock->area.width > 0);
+ g_assert(dock->area.height > 0);
+
+ XMoveResizeWindow(obt_display, dock->frame, dock->area.x, dock->area.y,
+ dock->area.width, dock->area.height);
+
+ RrPaint(dock->a_frame, dock->frame, dock->area.width,
+ dock->area.height);
+ XMapWindow(obt_display, dock->frame);
+ } else
+ XUnmapWindow(obt_display, dock->frame);
+
+ /* but they are useful outside of this function! but don't add it if the
+ dock is actually not visible */
+ if (dock->dock_apps) {
+ dock->area.width += ob_rr_theme->obwidth * 2;
+ dock->area.height += ob_rr_theme->obwidth * 2;
+ }
+
+ /* screen_resize() depends on this function to call screen_update_areas(),
+ so if this changes, also update screen_resize(). */
+ screen_update_areas();
+}
+
+void dock_app_configure(ObDockApp *app, gint w, gint h)
+{
+ app->w = w;
+ app->h = h;
+ dock_configure();
+}
+
+void dock_app_drag(ObDockApp *app, XMotionEvent *e)
+{
+ ObDockApp *over = NULL;
+ GList *it;
+ gint x, y;
+ gboolean after;
+ gboolean stop;
+
+ x = e->x_root;
+ y = e->y_root;
+
+ /* are we on top of the dock? */
+ if (!(x >= dock->area.x &&
+ y >= dock->area.y &&
+ x < dock->area.x + dock->area.width &&
+ y < dock->area.y + dock->area.height))
+ return;
+
+ x -= dock->area.x;
+ y -= dock->area.y;
+
+ /* which dock app are we on top of? */
+ stop = FALSE;
+ for (it = dock->dock_apps; it; it = g_list_next(it)) {
+ over = it->data;
+ switch (config_dock_orient) {
+ case OB_ORIENTATION_HORZ:
+ if (x >= over->x && x < over->x + over->w)
+ stop = TRUE;
+ break;
+ case OB_ORIENTATION_VERT:
+ if (y >= over->y && y < over->y + over->h)
+ stop = TRUE;
+ break;
+ }
+ /* dont go to it->next! */
+ if (stop) break;
+ }
+ if (!it || app == over) return;
+
+ x -= over->x;
+ y -= over->y;
+
+ switch (config_dock_orient) {
+ case OB_ORIENTATION_HORZ:
+ after = (x > over->w / 2);
+ break;
+ case OB_ORIENTATION_VERT:
+ after = (y > over->h / 2);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ /* remove before doing the it->next! */
+ dock->dock_apps = g_list_remove(dock->dock_apps, app);
+
+ if (after) it = it->next;
+
+ dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
+ dock_configure();
+}
+
+static gboolean hide_timeout(gpointer data)
+{
+ /* hide */
+ dock->hidden = TRUE;
+ dock_configure();
+
+ hide_timeout_id = 0;
+
+ return FALSE; /* don't repeat */
+}
+
+static gboolean show_timeout(gpointer data)
+{
+ /* show */
+ dock->hidden = FALSE;
+ dock_configure();
+
+ show_timeout_id = 0;
+
+ return FALSE; /* don't repeat */
+}
+
+void dock_hide(gboolean hide)
+{
+ if (!hide) {
+ if (dock->hidden && config_dock_hide) {
+ show_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ config_dock_show_delay,
+ show_timeout, NULL, NULL);
+ } else if (!dock->hidden && config_dock_hide && hide_timeout_id) {
+ if (hide_timeout_id) g_source_remove(hide_timeout_id);
+ hide_timeout_id = 0;
+ }
+ } else {
+ if (!dock->hidden && config_dock_hide) {
+ hide_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ config_dock_show_delay,
+ hide_timeout, NULL, NULL);
+ } else if (dock->hidden && config_dock_hide && show_timeout_id) {
+ if (show_timeout_id) g_source_remove(show_timeout_id);
+ show_timeout_id = 0;
+ }
+ }
+}
+
+void dock_get_area(Rect *a)
+{
+ RECT_SET(*a, dock->area.x, dock->area.y,
+ dock->area.width, dock->area.height);
+}
+
+void dock_raise_dock(void)
+{
+ stacking_raise(DOCK_AS_WINDOW(dock));
+}
+
+void dock_lower_dock(void)
+{
+ stacking_lower(DOCK_AS_WINDOW(dock));
+}
+
+ObDockApp* dock_find_dockapp(Window xwin)
+{
+ return g_hash_table_lookup(dock->dock_map, &xwin);
+}
diff --git a/openbox/dock.h b/openbox/dock.h
new file mode 100644
index 0000000..cb4fd6c
--- /dev/null
+++ b/openbox/dock.h
@@ -0,0 +1,88 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ dock.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __dock_h
+#define __dock_h
+
+#include "window.h"
+#include "stacking.h"
+#include "geom.h"
+#include "obrender/render.h"
+
+#include <glib.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+typedef struct _ObDock ObDock;
+typedef struct _ObDockApp ObDockApp;
+
+struct _ObDock
+{
+ ObWindow obwin;
+
+ Window frame;
+ RrAppearance *a_frame;
+
+ /* actual position (when not auto-hidden) */
+ Rect area;
+
+ gboolean hidden;
+
+ GList *dock_apps;
+ GHashTable *dock_map;
+};
+
+struct _ObDockApp {
+ gint ignore_unmaps;
+
+ Window icon_win;
+ Window name_win;
+
+ gchar *name;
+ gchar *class;
+
+ gint x;
+ gint y;
+ gint w;
+ gint h;
+};
+
+extern StrutPartial dock_strut;
+
+void dock_startup(gboolean reconfig);
+void dock_shutdown(gboolean reconfig);
+
+void dock_configure(void);
+void dock_hide(gboolean hide);
+
+void dock_manage(Window icon_win, Window name_win);
+
+void dock_unmanage_all(void);
+void dock_unmanage(ObDockApp *app, gboolean reparent);
+
+void dock_app_drag(ObDockApp *app, XMotionEvent *e);
+void dock_app_configure(ObDockApp *app, gint w, gint h);
+
+void dock_get_area(Rect *a);
+
+void dock_raise_dock(void);
+void dock_lower_dock(void);
+
+ObDockApp* dock_find_dockapp(Window xwin);
+
+#endif
diff --git a/openbox/event.c b/openbox/event.c
new file mode 100644
index 0000000..cf089b6
--- /dev/null
+++ b/openbox/event.c
@@ -0,0 +1,2287 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ event.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "event.h"
+#include "debug.h"
+#include "window.h"
+#include "openbox.h"
+#include "dock.h"
+#include "actions.h"
+#include "client.h"
+#include "config.h"
+#include "screen.h"
+#include "frame.h"
+#include "grab.h"
+#include "menu.h"
+#include "prompt.h"
+#include "menuframe.h"
+#include "keyboard.h"
+#include "mouse.h"
+#include "focus.h"
+#include "focus_cycle.h"
+#include "moveresize.h"
+#include "group.h"
+#include "stacking.h"
+#include "ping.h"
+#include "obt/display.h"
+#include "obt/xqueue.h"
+#include "obt/prop.h"
+#include "obt/keyboard.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <glib.h>
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h> /* for usleep() */
+#endif
+
+#ifdef USE_SM
+#include <X11/ICE/ICElib.h>
+#endif
+
+typedef struct
+{
+ gboolean ignored;
+} ObEventData;
+
+typedef struct
+{
+ ObClient *client;
+ Time time;
+ gulong serial;
+} ObFocusDelayData;
+
+typedef struct
+{
+ gulong start; /* inclusive */
+ gulong end; /* inclusive */
+} ObSerialRange;
+
+static void event_process(const XEvent *e, gpointer data);
+static void event_handle_root(XEvent *e);
+static gboolean event_handle_menu_input(XEvent *e);
+static void event_handle_menu(ObMenuFrame *frame, XEvent *e);
+static gboolean event_handle_prompt(ObPrompt *p, XEvent *e);
+static void event_handle_dock(ObDock *s, XEvent *e);
+static void event_handle_dockapp(ObDockApp *app, XEvent *e);
+static void event_handle_client(ObClient *c, XEvent *e);
+static gboolean event_handle_user_input(ObClient *client, XEvent *e);
+static gboolean is_enter_focus_event_ignored(gulong serial);
+static void event_ignore_enter_range(gulong start, gulong end);
+
+static void focus_delay_dest(gpointer data);
+static void unfocus_delay_dest(gpointer data);
+static gboolean focus_delay_func(gpointer data);
+static gboolean unfocus_delay_func(gpointer data);
+static void focus_delay_client_dest(ObClient *client, gpointer data);
+
+Time event_last_user_time = CurrentTime;
+
+/*! The time of the current X event (if it had a timestamp) */
+static Time event_curtime = CurrentTime;
+/*! The source time that started the current X event (user-provided, so not
+ to be trusted) */
+static Time event_sourcetime = CurrentTime;
+
+/*! The serial of the current X event */
+static gulong event_curserial;
+static gboolean focus_left_screen = FALSE;
+static gboolean waiting_for_focusin = FALSE;
+/*! A list of ObSerialRanges which are to be ignored for mouse enter events */
+static GSList *ignore_serials = NULL;
+static guint focus_delay_timeout_id = 0;
+static ObClient *focus_delay_timeout_client = NULL;
+static guint unfocus_delay_timeout_id = 0;
+static ObClient *unfocus_delay_timeout_client = NULL;
+
+#ifdef USE_SM
+static gboolean ice_handler(GIOChannel *source, GIOCondition cond,
+ gpointer conn)
+{
+ Bool b;
+ IceProcessMessages(conn, NULL, &b);
+ return TRUE; /* don't remove the event source */
+}
+
+static void ice_watch(IceConn conn, IcePointer data, Bool opening,
+ IcePointer *watch_data)
+{
+ static guint id = 0;
+
+ if (opening) {
+ GIOChannel *ch;
+
+ ch = g_io_channel_unix_new(IceConnectionNumber(conn));
+ id = g_io_add_watch(ch, G_IO_IN, ice_handler, conn);
+ g_io_channel_unref(ch);
+ } else if (id) {
+ g_source_remove(id);
+ id = 0;
+ }
+}
+#endif
+
+void event_startup(gboolean reconfig)
+{
+ if (reconfig) return;
+
+ xqueue_add_callback(event_process, NULL);
+
+#ifdef USE_SM
+ IceAddConnectionWatch(ice_watch, NULL);
+#endif
+
+ client_add_destroy_notify(focus_delay_client_dest, NULL);
+}
+
+void event_shutdown(gboolean reconfig)
+{
+ if (reconfig) return;
+
+#ifdef USE_SM
+ IceRemoveConnectionWatch(ice_watch, NULL);
+#endif
+
+ client_remove_destroy_notify(focus_delay_client_dest);
+}
+
+static Window event_get_window(XEvent *e)
+{
+ Window window;
+
+ /* pick a window */
+ switch (e->type) {
+ case SelectionClear:
+ window = obt_root(ob_screen);
+ break;
+ case CreateNotify:
+ window = e->xcreatewindow.window;
+ break;
+ case MapRequest:
+ window = e->xmaprequest.window;
+ break;
+ case MapNotify:
+ window = e->xmap.window;
+ break;
+ case UnmapNotify:
+ window = e->xunmap.window;
+ break;
+ case DestroyNotify:
+ window = e->xdestroywindow.window;
+ break;
+ case ConfigureRequest:
+ window = e->xconfigurerequest.window;
+ break;
+ case ConfigureNotify:
+ window = e->xconfigure.window;
+ break;
+ default:
+#ifdef XKB
+ if (obt_display_extension_xkb &&
+ e->type == obt_display_extension_xkb_basep)
+ {
+ switch (((XkbAnyEvent*)e)->xkb_type) {
+ case XkbBellNotify:
+ window = ((XkbBellNotifyEvent*)e)->window;
+ default:
+ window = None;
+ }
+ } else
+#endif
+#ifdef SYNC
+ if (obt_display_extension_sync &&
+ e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
+ {
+ window = None;
+ } else
+#endif
+ window = e->xany.window;
+ }
+ return window;
+}
+
+static inline Time event_get_timestamp(const XEvent *e)
+{
+ Time t = CurrentTime;
+
+ /* grab the lasttime and hack up the state */
+ switch (e->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ t = e->xbutton.time;
+ break;
+ case KeyPress:
+ t = e->xkey.time;
+ break;
+ case KeyRelease:
+ t = e->xkey.time;
+ break;
+ case MotionNotify:
+ t = e->xmotion.time;
+ break;
+ case PropertyNotify:
+ t = e->xproperty.time;
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ t = e->xcrossing.time;
+ break;
+ default:
+#ifdef SYNC
+ if (obt_display_extension_sync &&
+ e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
+ {
+ t = ((const XSyncAlarmNotifyEvent*)e)->time;
+ }
+#endif
+ /* if more event types are anticipated, get their timestamp
+ explicitly */
+ break;
+ }
+
+ return t;
+}
+
+static void event_set_curtime(XEvent *e)
+{
+ Time t = event_get_timestamp(e);
+
+ /* watch that if we get an event earlier than the last specified user_time,
+ which can happen if the clock goes backwards, we erase the last
+ specified user_time */
+ if (t && event_last_user_time && event_time_after(event_last_user_time, t))
+ event_reset_user_time();
+
+ event_sourcetime = CurrentTime;
+ event_curtime = t;
+}
+
+static void event_hack_mods(XEvent *e)
+{
+ switch (e->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ e->xbutton.state = obt_keyboard_only_modmasks(e->xbutton.state);
+ break;
+ case KeyPress:
+ break;
+ case KeyRelease:
+ break;
+ case MotionNotify:
+ e->xmotion.state = obt_keyboard_only_modmasks(e->xmotion.state);
+ /* compress events */
+ {
+ XEvent ce;
+ ObtXQueueWindowType wt;
+
+ wt.window = e->xmotion.window;
+ wt.type = MotionNotify;
+ while (xqueue_remove_local(&ce, xqueue_match_window_type, &wt)) {
+ e->xmotion.x = ce.xmotion.x;
+ e->xmotion.y = ce.xmotion.y;
+ e->xmotion.x_root = ce.xmotion.x_root;
+ e->xmotion.y_root = ce.xmotion.y_root;
+ }
+ }
+ break;
+ }
+}
+
+static gboolean wanted_focusevent(XEvent *e, gboolean in_client_only)
+{
+ gint mode = e->xfocus.mode;
+ gint detail = e->xfocus.detail;
+ Window win = e->xany.window;
+
+ if (e->type == FocusIn) {
+ /* These are ones we never want.. */
+
+ /* This means focus was given by a keyboard/mouse grab. */
+ if (mode == NotifyGrab)
+ return FALSE;
+ /* This means focus was given back from a keyboard/mouse grab. */
+ if (mode == NotifyUngrab)
+ return FALSE;
+
+ /* These are the ones we want.. */
+
+ if (win == obt_root(ob_screen)) {
+ /* If looking for a focus in on a client, then always return
+ FALSE for focus in's to the root window */
+ if (in_client_only)
+ return FALSE;
+ /* This means focus reverted off of a client */
+ else if (detail == NotifyPointerRoot ||
+ detail == NotifyDetailNone ||
+ detail == NotifyInferior ||
+ /* This means focus got here from another screen */
+ detail == NotifyNonlinear)
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ /* It was on a client, was it a valid one?
+ It's possible to get a FocusIn event for a client that was managed
+ but has disappeared.
+ */
+ if (in_client_only) {
+ ObWindow *w = window_find(e->xfocus.window);
+ if (!w || !WINDOW_IS_CLIENT(w))
+ return FALSE;
+ }
+ else {
+ /* This means focus reverted to parent from the client (this
+ happens often during iconify animation) */
+ if (detail == NotifyInferior)
+ return TRUE;
+ }
+
+ /* This means focus moved from the root window to a client */
+ if (detail == NotifyVirtual)
+ return TRUE;
+ /* This means focus moved from one client to another */
+ if (detail == NotifyNonlinearVirtual)
+ return TRUE;
+
+ /* Otherwise.. */
+ return FALSE;
+ } else {
+ g_assert(e->type == FocusOut);
+
+ /* These are ones we never want.. */
+
+ /* This means focus was taken by a keyboard/mouse grab. */
+ if (mode == NotifyGrab)
+ return FALSE;
+ /* This means focus was grabbed on a window and it was released. */
+ if (mode == NotifyUngrab)
+ return FALSE;
+
+ /* Focus left the root window revertedto state */
+ if (win == obt_root(ob_screen))
+ return FALSE;
+
+ /* These are the ones we want.. */
+
+ /* This means focus moved from a client to the root window */
+ if (detail == NotifyVirtual)
+ return TRUE;
+ /* This means focus moved from one client to another */
+ if (detail == NotifyNonlinearVirtual)
+ return TRUE;
+
+ /* Otherwise.. */
+ return FALSE;
+ }
+}
+
+static gboolean event_look_for_focusin(XEvent *e, gpointer data)
+{
+ return e->type == FocusIn && wanted_focusevent(e, FALSE);
+}
+
+static gboolean event_look_for_focusin_client(XEvent *e, gpointer data)
+{
+ return e->type == FocusIn && wanted_focusevent(e, TRUE);
+}
+
+static void print_focusevent(XEvent *e)
+{
+ gint mode = e->xfocus.mode;
+ gint detail = e->xfocus.detail;
+ Window win = e->xany.window;
+ const gchar *modestr, *detailstr;
+
+ switch (mode) {
+ case NotifyNormal: modestr="NotifyNormal"; break;
+ case NotifyGrab: modestr="NotifyGrab"; break;
+ case NotifyUngrab: modestr="NotifyUngrab"; break;
+ case NotifyWhileGrabbed: modestr="NotifyWhileGrabbed"; break;
+ default: g_assert_not_reached();
+ }
+ switch (detail) {
+ case NotifyAncestor: detailstr="NotifyAncestor"; break;
+ case NotifyVirtual: detailstr="NotifyVirtual"; break;
+ case NotifyInferior: detailstr="NotifyInferior"; break;
+ case NotifyNonlinear: detailstr="NotifyNonlinear"; break;
+ case NotifyNonlinearVirtual: detailstr="NotifyNonlinearVirtual"; break;
+ case NotifyPointer: detailstr="NotifyPointer"; break;
+ case NotifyPointerRoot: detailstr="NotifyPointerRoot"; break;
+ case NotifyDetailNone: detailstr="NotifyDetailNone"; break;
+ default: g_assert_not_reached();
+ }
+
+ if (mode == NotifyGrab || mode == NotifyUngrab)
+ return;
+
+ g_assert(modestr);
+ g_assert(detailstr);
+ ob_debug_type(OB_DEBUG_FOCUS, "Focus%s 0x%x mode=%s detail=%s",
+ (e->xfocus.type == FocusIn ? "In" : "Out"),
+ win,
+ modestr, detailstr);
+
+}
+
+static void event_process(const XEvent *ec, gpointer data)
+{
+ XEvent ee, *e;
+ Window window;
+ ObClient *client = NULL;
+ ObDock *dock = NULL;
+ ObDockApp *dockapp = NULL;
+ ObWindow *obwin = NULL;
+ ObMenuFrame *menu = NULL;
+ ObPrompt *prompt = NULL;
+ gboolean used;
+
+ /* make a copy we can mangle */
+ ee = *ec;
+ e = &ee;
+
+ window = event_get_window(e);
+ if (window == obt_root(ob_screen))
+ /* don't do any lookups, waste of cpu */;
+ else if ((obwin = window_find(window))) {
+ switch (obwin->type) {
+ case OB_WINDOW_CLASS_DOCK:
+ dock = WINDOW_AS_DOCK(obwin);
+ break;
+ case OB_WINDOW_CLASS_CLIENT:
+ client = WINDOW_AS_CLIENT(obwin);
+ /* events on clients can be events on prompt windows too */
+ prompt = client->prompt;
+ break;
+ case OB_WINDOW_CLASS_MENUFRAME:
+ menu = WINDOW_AS_MENUFRAME(obwin);
+ break;
+ case OB_WINDOW_CLASS_INTERNAL:
+ /* we don't do anything with events directly on these windows */
+ break;
+ case OB_WINDOW_CLASS_PROMPT:
+ prompt = WINDOW_AS_PROMPT(obwin);
+ break;
+ }
+ }
+ else
+ dockapp = dock_find_dockapp(window);
+
+ event_set_curtime(e);
+ event_curserial = e->xany.serial;
+ event_hack_mods(e);
+
+ /* deal with it in the kernel */
+
+ if (e->type == FocusIn) {
+ print_focusevent(e);
+ if (!wanted_focusevent(e, FALSE)) {
+ if (waiting_for_focusin) {
+ /* We were waiting for this FocusIn, since we got a FocusOut
+ earlier, but it went to a window that isn't a client. */
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focus went to an unmanaged window 0x%x !",
+ e->xfocus.window);
+ focus_fallback(TRUE, config_focus_under_mouse, TRUE, TRUE);
+ }
+ }
+ else if (client && e->xfocus.detail == NotifyInferior) {
+ ob_debug_type(OB_DEBUG_FOCUS, "Focus went to the frame window");
+
+ focus_left_screen = FALSE;
+
+ focus_fallback(FALSE, config_focus_under_mouse, TRUE, TRUE);
+
+ /* We don't get a FocusOut for this case, because it's just moving
+ from our Inferior up to us. This happens when iconifying a
+ window with RevertToParent focus */
+ frame_adjust_focus(client->frame, FALSE);
+ /* focus_set_client(NULL) has already been called */
+ }
+ else if (e->xfocus.detail == NotifyPointerRoot ||
+ e->xfocus.detail == NotifyDetailNone ||
+ e->xfocus.detail == NotifyInferior ||
+ e->xfocus.detail == NotifyNonlinear)
+ {
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focus went to root or pointer root/none");
+
+ if (e->xfocus.detail == NotifyInferior ||
+ e->xfocus.detail == NotifyNonlinear)
+ {
+ focus_left_screen = FALSE;
+ }
+
+ /* If another FocusIn is in the queue then don't fallback yet. This
+ fixes the fun case of:
+ window map -> send focusin
+ window unmap -> get focusout
+ window map -> send focusin
+ get first focus out -> fall back to something (new window
+ hasn't received focus yet, so something else) -> send focusin
+ which means the "something else" is the last thing to get a
+ focusin sent to it, so the new window doesn't end up with focus.
+
+ But if the other focus in is something like PointerRoot then we
+ still want to fall back.
+ */
+ if (xqueue_exists_local(event_look_for_focusin_client, NULL)) {
+ ob_debug_type(OB_DEBUG_FOCUS,
+ " but another FocusIn is coming");
+ } else {
+ /* Focus has been reverted.
+
+ FocusOut events come after UnmapNotify, so we don't need to
+ worry about focusing an invalid window
+ */
+
+ if (!focus_left_screen)
+ focus_fallback(FALSE, config_focus_under_mouse,
+ TRUE, TRUE);
+ }
+ }
+ else if (!client)
+ {
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focus went to a window that is already gone");
+
+ /* If you send focus to a window and then it disappears, you can
+ get the FocusIn for it, after it is unmanaged.
+ Just wait for the next FocusOut/FocusIn pair, but make note that
+ the window that was focused no longer is. */
+ focus_set_client(NULL);
+ }
+ else if (client != focus_client) {
+ focus_left_screen = FALSE;
+ frame_adjust_focus(client->frame, TRUE);
+ focus_set_client(client);
+ client_calc_layer(client);
+ client_bring_helper_windows(client);
+ }
+
+ waiting_for_focusin = FALSE;
+ } else if (e->type == FocusOut) {
+ print_focusevent(e);
+ if (!wanted_focusevent(e, FALSE))
+ ; /* skip this one */
+ /* Look for the followup FocusIn */
+ else if (!xqueue_exists_local(event_look_for_focusin, NULL)) {
+ /* There is no FocusIn, this means focus went to a window that
+ is not being managed, or a window on another screen. */
+ Window win, root;
+ gint i;
+ guint u;
+ obt_display_ignore_errors(TRUE);
+ if (XGetInputFocus(obt_display, &win, &i) &&
+ XGetGeometry(obt_display, win, &root, &i,&i,&u,&u,&u,&u) &&
+ root != obt_root(ob_screen))
+ {
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focus went to another screen !");
+ focus_left_screen = TRUE;
+ }
+ else
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focus went to a black hole !");
+ obt_display_ignore_errors(FALSE);
+ /* nothing is focused */
+ focus_set_client(NULL);
+ } else {
+ /* Focus moved, so mark that we are waiting to process that
+ FocusIn */
+ waiting_for_focusin = TRUE;
+
+ /* nothing is focused right now, but will be again shortly */
+ focus_set_client(NULL);
+ }
+
+ if (client && client != focus_client)
+ frame_adjust_focus(client->frame, FALSE);
+ }
+ else if (client)
+ event_handle_client(client, e);
+ else if (dockapp)
+ event_handle_dockapp(dockapp, e);
+ else if (dock)
+ event_handle_dock(dock, e);
+ else if (menu)
+ event_handle_menu(menu, e);
+ else if (window == obt_root(ob_screen))
+ event_handle_root(e);
+ else if (e->type == MapRequest)
+ window_manage(window);
+ else if (e->type == MappingNotify) {
+ /* keyboard layout changes for modifier mapping changes. reload the
+ modifier map, and rebind all the key bindings as appropriate */
+ ob_debug("Keyboard map changed. Reloading keyboard bindings.");
+ ob_set_state(OB_STATE_RECONFIGURING);
+ obt_keyboard_reload();
+ keyboard_rebind();
+ ob_set_state(OB_STATE_RUNNING);
+ }
+ else if (e->type == ClientMessage) {
+ /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
+ windows that are not managed yet. */
+ if (e->xclient.message_type ==
+ OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS))
+ {
+ /* Pretend to manage the client, getting information used to
+ determine its decorations */
+ ObClient *c = client_fake_manage(e->xclient.window);
+ gulong vals[4];
+
+ /* set the frame extents on the window */
+ vals[0] = c->frame->size.left;
+ vals[1] = c->frame->size.right;
+ vals[2] = c->frame->size.top;
+ vals[3] = c->frame->size.bottom;
+ OBT_PROP_SETA32(e->xclient.window, NET_FRAME_EXTENTS,
+ CARDINAL, vals, 4);
+
+ /* Free the pretend client */
+ client_fake_unmanage(c);
+ }
+ }
+ else if (e->type == ConfigureRequest) {
+ /* unhandled configure requests must be used to configure the
+ window directly */
+ XWindowChanges xwc;
+
+ xwc.x = e->xconfigurerequest.x;
+ xwc.y = e->xconfigurerequest.y;
+ xwc.width = e->xconfigurerequest.width;
+ xwc.height = e->xconfigurerequest.height;
+ xwc.border_width = e->xconfigurerequest.border_width;
+ xwc.sibling = e->xconfigurerequest.above;
+ xwc.stack_mode = e->xconfigurerequest.detail;
+
+ /* we are not to be held responsible if someone sends us an
+ invalid request! */
+ obt_display_ignore_errors(TRUE);
+ XConfigureWindow(obt_display, window,
+ e->xconfigurerequest.value_mask, &xwc);
+ obt_display_ignore_errors(FALSE);
+ }
+#ifdef SYNC
+ else if (obt_display_extension_sync &&
+ e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
+ {
+ XSyncAlarmNotifyEvent *se = (XSyncAlarmNotifyEvent*)e;
+ if (se->alarm == moveresize_alarm && moveresize_in_progress)
+ moveresize_event(e);
+ }
+#endif
+
+ if (e->type == ButtonPress || e->type == ButtonRelease) {
+ ObWindow *w;
+ static guint pressed = 0;
+ static Window pressed_win = None;
+
+ event_sourcetime = event_curtime;
+
+ /* If the button press was on some non-root window, or was physically
+ on the root window... */
+ if (window != obt_root(ob_screen) ||
+ e->xbutton.subwindow == None ||
+ /* ...or if it is related to the last button press we handled... */
+ pressed == e->xbutton.button ||
+ /* ...or it if it was physically on an openbox
+ internal window... */
+ ((w = window_find(e->xbutton.subwindow)) &&
+ (WINDOW_IS_INTERNAL(w) || WINDOW_IS_DOCK(w))))
+ /* ...then process the event, otherwise ignore it */
+ {
+ used = event_handle_user_input(client, e);
+
+ if (prompt && !used)
+ used = event_handle_prompt(prompt, e);
+
+ if (e->type == ButtonPress) {
+ pressed = e->xbutton.button;
+ pressed_win = e->xbutton.subwindow;
+ }
+ }
+ }
+ else if (e->type == KeyPress || e->type == KeyRelease ||
+ e->type == MotionNotify)
+ {
+ event_sourcetime = event_curtime;
+
+ used = event_handle_user_input(client, e);
+
+ if (prompt && !used)
+ used = event_handle_prompt(prompt, e);
+ }
+
+ /* show any debug prompts that are queued */
+ ob_debug_show_prompts();
+
+ /* if something happens and it's not from an XEvent, then we don't know
+ the time, so clear it here until the next event is handled */
+ event_curtime = event_sourcetime = CurrentTime;
+ event_curserial = 0;
+}
+
+static void event_handle_root(XEvent *e)
+{
+ Atom msgtype;
+
+ switch(e->type) {
+ case SelectionClear:
+ ob_debug("Another WM has requested to replace us. Exiting.");
+ ob_exit_replace();
+ break;
+
+ case ClientMessage:
+ if (e->xclient.format != 32) break;
+
+ msgtype = e->xclient.message_type;
+ if (msgtype == OBT_PROP_ATOM(NET_CURRENT_DESKTOP)) {
+ guint d = e->xclient.data.l[0];
+ if (d < screen_num_desktops) {
+ if (e->xclient.data.l[1] == 0)
+ ob_debug_type(OB_DEBUG_APP_BUGS,
+ "_NET_CURRENT_DESKTOP message is missing "
+ "a timestamp");
+ else
+ event_sourcetime = e->xclient.data.l[1];
+ screen_set_desktop(d, TRUE);
+ }
+ } else if (msgtype == OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS)) {
+ guint d = e->xclient.data.l[0];
+ if (d > 0 && d <= 1000)
+ screen_set_num_desktops(d);
+ } else if (msgtype == OBT_PROP_ATOM(NET_SHOWING_DESKTOP)) {
+ screen_show_desktop(e->xclient.data.l[0] != 0, NULL);
+ } else if (msgtype == OBT_PROP_ATOM(OB_CONTROL)) {
+ ob_debug("OB_CONTROL: %d", e->xclient.data.l[0]);
+ if (e->xclient.data.l[0] == 1)
+ ob_reconfigure();
+ else if (e->xclient.data.l[0] == 2)
+ ob_restart();
+ else if (e->xclient.data.l[0] == 3)
+ ob_exit(0);
+ } else if (msgtype == OBT_PROP_ATOM(WM_PROTOCOLS)) {
+ if ((Atom)e->xclient.data.l[0] == OBT_PROP_ATOM(NET_WM_PING))
+ ping_got_pong(e->xclient.data.l[1]);
+ }
+ break;
+ case PropertyNotify:
+ if (e->xproperty.atom == OBT_PROP_ATOM(NET_DESKTOP_NAMES)) {
+ ob_debug("UPDATE DESKTOP NAMES");
+ screen_update_desktop_names();
+ }
+ else if (e->xproperty.atom == OBT_PROP_ATOM(NET_DESKTOP_LAYOUT))
+ screen_update_layout();
+ break;
+ case ConfigureNotify:
+#ifdef XRANDR
+ XRRUpdateConfiguration(e);
+#endif
+ screen_resize();
+ break;
+ default:
+ ;
+ }
+}
+
+void event_enter_client(ObClient *client)
+{
+ 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"
+ "on client 0x%x", event_curserial, client->window);
+ return;
+ }
+
+ if (client_enter_focusable(client) && client_can_focus(client)) {
+ if (config_focus_delay) {
+ ObFocusDelayData *data;
+
+ if (focus_delay_timeout_id)
+ g_source_remove(focus_delay_timeout_id);
+
+ data = g_slice_new(ObFocusDelayData);
+ data->client = client;
+ data->time = event_time();
+ data->serial = event_curserial;
+
+ focus_delay_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ config_focus_delay,
+ focus_delay_func,
+ data,
+ focus_delay_dest);
+ focus_delay_timeout_client = client;
+ } else {
+ ObFocusDelayData data;
+ data.client = client;
+ data.time = event_time();
+ data.serial = event_curserial;
+ focus_delay_func(&data);
+ }
+ }
+}
+
+void event_leave_client(ObClient *client)
+{
+ g_assert(config_focus_follow);
+
+ if (is_enter_focus_event_ignored(event_curserial)) {
+ ob_debug_type(OB_DEBUG_FOCUS, "Ignoring leave event with serial %lu\n"
+ "on client 0x%x", event_curserial, client->window);
+ return;
+ }
+
+ if (client == focus_client) {
+ if (config_focus_delay) {
+ ObFocusDelayData *data;
+
+ if (unfocus_delay_timeout_id)
+ g_source_remove(unfocus_delay_timeout_id);
+
+ data = g_slice_new(ObFocusDelayData);
+ data->client = client;
+ data->time = event_time();
+ data->serial = event_curserial;
+
+ unfocus_delay_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ config_focus_delay,
+ unfocus_delay_func,
+ data,
+ unfocus_delay_dest);
+ unfocus_delay_timeout_client = client;
+ } else {
+ ObFocusDelayData data;
+ data.client = client;
+ data.time = event_time();
+ data.serial = event_curserial;
+ unfocus_delay_func(&data);
+ }
+ }
+}
+
+static gboolean *context_to_button(ObFrame *f, ObFrameContext con, gboolean press)
+{
+ if (press) {
+ switch (con) {
+ case OB_FRAME_CONTEXT_MAXIMIZE:
+ return &f->max_press;
+ case OB_FRAME_CONTEXT_CLOSE:
+ return &f->close_press;
+ case OB_FRAME_CONTEXT_ICONIFY:
+ return &f->iconify_press;
+ case OB_FRAME_CONTEXT_ALLDESKTOPS:
+ return &f->desk_press;
+ case OB_FRAME_CONTEXT_SHADE:
+ return &f->shade_press;
+ default:
+ return NULL;
+ }
+ } else {
+ switch (con) {
+ case OB_FRAME_CONTEXT_MAXIMIZE:
+ return &f->max_hover;
+ case OB_FRAME_CONTEXT_CLOSE:
+ return &f->close_hover;
+ case OB_FRAME_CONTEXT_ICONIFY:
+ return &f->iconify_hover;
+ case OB_FRAME_CONTEXT_ALLDESKTOPS:
+ return &f->desk_hover;
+ case OB_FRAME_CONTEXT_SHADE:
+ return &f->shade_hover;
+ default:
+ return NULL;
+ }
+ }
+}
+
+static gboolean more_client_message_event(Window window, Atom msgtype)
+{
+ ObtXQueueWindowMessage wm;
+ wm.window = window;
+ wm.message = msgtype;
+ return xqueue_exists_local(xqueue_match_window_message, &wm);
+}
+
+struct ObSkipPropertyChange {
+ Window window;
+ Atom prop;
+};
+
+static gboolean skip_property_change(XEvent *e, gpointer data)
+{
+ const struct ObSkipPropertyChange s = *(struct ObSkipPropertyChange*)data;
+
+ if (e->type == PropertyNotify && e->xproperty.window == s.window) {
+ const Atom a = e->xproperty.atom;
+ const Atom b = s.prop;
+
+ /* these are all updated together */
+ if ((a == OBT_PROP_ATOM(NET_WM_NAME) ||
+ a == OBT_PROP_ATOM(WM_NAME) ||
+ a == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
+ a == OBT_PROP_ATOM(WM_ICON_NAME))
+ &&
+ (b == OBT_PROP_ATOM(NET_WM_NAME) ||
+ b == OBT_PROP_ATOM(WM_NAME) ||
+ b == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
+ b == OBT_PROP_ATOM(WM_ICON_NAME)))
+ {
+ return TRUE;
+ }
+ else if (a == b && a == OBT_PROP_ATOM(NET_WM_ICON))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void event_handle_client(ObClient *client, XEvent *e)
+{
+ Atom msgtype;
+ ObFrameContext con;
+ gboolean *but;
+ static gint px = -1, py = -1;
+ static guint pb = 0;
+ static ObFrameContext pcon = OB_FRAME_CONTEXT_NONE;
+
+ switch (e->type) {
+ case ButtonPress:
+ /* save where the press occured for the first button pressed */
+ if (!pb) {
+ pb = e->xbutton.button;
+ px = e->xbutton.x;
+ py = e->xbutton.y;
+
+ pcon = frame_context(client, e->xbutton.window, px, py);
+ pcon = mouse_button_frame_context(pcon, e->xbutton.button,
+ e->xbutton.state);
+ }
+ case ButtonRelease:
+ /* Wheel buttons don't draw because they are an instant click, so it
+ is a waste of resources to go drawing it.
+ if the user is doing an interactive thing, or has a menu open then
+ the mouse is grabbed (possibly) and if we get these events we don't
+ want to deal with them
+ */
+ if (!(e->xbutton.button == 4 || e->xbutton.button == 5) &&
+ !grab_on_keyboard())
+ {
+ /* use where the press occured */
+ con = frame_context(client, e->xbutton.window, px, py);
+ con = mouse_button_frame_context(con, e->xbutton.button,
+ e->xbutton.state);
+
+ /* button presses on CLIENT_CONTEXTs are not accompanied by a
+ release because they are Replayed to the client */
+ if ((e->type == ButtonRelease || CLIENT_CONTEXT(con, client)) &&
+ e->xbutton.button == pb)
+ pb = 0, px = py = -1, pcon = OB_FRAME_CONTEXT_NONE;
+
+ but = context_to_button(client->frame, con, TRUE);
+ if (but) {
+ *but = (e->type == ButtonPress);
+ frame_adjust_state(client->frame);
+ }
+ }
+ break;
+ case MotionNotify:
+ /* when there is a grab on the pointer, we won't get enter/leave
+ notifies, but we still get motion events */
+ if (grab_on_pointer()) break;
+
+ con = frame_context(client, e->xmotion.window,
+ e->xmotion.x, e->xmotion.y);
+ switch (con) {
+ case OB_FRAME_CONTEXT_TITLEBAR:
+ case OB_FRAME_CONTEXT_TLCORNER:
+ case OB_FRAME_CONTEXT_TRCORNER:
+ /* we've left the button area inside the titlebar */
+ if (client->frame->max_hover || client->frame->desk_hover ||
+ client->frame->shade_hover || client->frame->iconify_hover ||
+ client->frame->close_hover)
+ {
+ client->frame->max_hover =
+ client->frame->desk_hover =
+ client->frame->shade_hover =
+ client->frame->iconify_hover =
+ client->frame->close_hover = FALSE;
+ frame_adjust_state(client->frame);
+ }
+ break;
+ default:
+ but = context_to_button(client->frame, con, FALSE);
+ if (but && !*but && !pb) {
+ *but = TRUE;
+ frame_adjust_state(client->frame);
+ }
+ break;
+ }
+ break;
+ case LeaveNotify:
+ con = frame_context(client, e->xcrossing.window,
+ e->xcrossing.x, e->xcrossing.y);
+ switch (con) {
+ case OB_FRAME_CONTEXT_TITLEBAR:
+ case OB_FRAME_CONTEXT_TLCORNER:
+ case OB_FRAME_CONTEXT_TRCORNER:
+ /* we've left the button area inside the titlebar */
+ client->frame->max_hover =
+ client->frame->desk_hover =
+ client->frame->shade_hover =
+ client->frame->iconify_hover =
+ client->frame->close_hover = FALSE;
+ if (e->xcrossing.mode == NotifyGrab) {
+ client->frame->max_press =
+ client->frame->desk_press =
+ client->frame->shade_press =
+ client->frame->iconify_press =
+ client->frame->close_press = FALSE;
+ }
+ break;
+ case OB_FRAME_CONTEXT_FRAME:
+ /* When the mouse leaves an animating window, don't use the
+ corresponding enter events. Pretend like the animating window
+ doesn't even exist..! */
+ if (frame_iconify_animating(client->frame))
+ event_end_ignore_all_enters(event_start_ignore_all_enters());
+
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "%sNotify mode %d detail %d on %lx",
+ (e->type == EnterNotify ? "Enter" : "Leave"),
+ e->xcrossing.mode,
+ e->xcrossing.detail, (client?client->window:0));
+ if (grab_on_keyboard())
+ break;
+ if (config_focus_follow &&
+ /* leave inferior events can happen when the mouse goes onto
+ the window's border and then into the window before the
+ delay is up */
+ e->xcrossing.detail != NotifyInferior)
+ {
+ if (config_focus_delay && focus_delay_timeout_id)
+ g_source_remove(focus_delay_timeout_id);
+ if (config_unfocus_leave)
+ event_leave_client(client);
+ }
+ break;
+ default:
+ but = context_to_button(client->frame, con, FALSE);
+ if (but) {
+ *but = FALSE;
+ if (e->xcrossing.mode == NotifyGrab) {
+ but = context_to_button(client->frame, con, TRUE);
+ *but = FALSE;
+ }
+ frame_adjust_state(client->frame);
+ }
+ break;
+ }
+ break;
+ case EnterNotify:
+ {
+ con = frame_context(client, e->xcrossing.window,
+ e->xcrossing.x, e->xcrossing.y);
+ switch (con) {
+ case OB_FRAME_CONTEXT_FRAME:
+ if (grab_on_keyboard())
+ break;
+ if (e->xcrossing.mode == NotifyGrab ||
+ (e->xcrossing.mode == NotifyUngrab &&
+ /* ungrab enters are used when _under_ mouse is being used */
+ !(config_focus_follow && config_focus_under_mouse)) ||
+ /*ignore enters when we're already in the window */
+ e->xcrossing.detail == NotifyInferior)
+ {
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "%sNotify mode %d detail %d serial %lu on %lx "
+ "IGNORED",
+ (e->type == EnterNotify ? "Enter" : "Leave"),
+ e->xcrossing.mode,
+ e->xcrossing.detail,
+ e->xcrossing.serial,
+ client?client->window:0);
+ }
+ else {
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "%sNotify mode %d detail %d serial %lu on %lx, "
+ "focusing window",
+ (e->type == EnterNotify ? "Enter" : "Leave"),
+ e->xcrossing.mode,
+ e->xcrossing.detail,
+ e->xcrossing.serial,
+ (client?client->window:0));
+ if (config_focus_follow) {
+ if (config_focus_delay && unfocus_delay_timeout_id)
+ g_source_remove(unfocus_delay_timeout_id);
+ event_enter_client(client);
+ }
+ }
+ break;
+ default:
+ but = context_to_button(client->frame, con, FALSE);
+ if (but) {
+ *but = TRUE;
+ if (e->xcrossing.mode == NotifyUngrab) {
+ but = context_to_button(client->frame, con, TRUE);
+ *but = (con == pcon);
+ }
+ frame_adjust_state(client->frame);
+ }
+ break;
+ }
+ break;
+ }
+ case ConfigureRequest:
+ {
+ /* dont compress these unless you're going to watch for property
+ notifies in between (these can change what the configure would
+ do to the window).
+ also you can't compress stacking events
+ */
+
+ gint x, y, w, h;
+ gboolean move = FALSE;
+ gboolean resize = FALSE;
+
+ /* get the current area */
+ RECT_TO_DIMS(client->area, x, y, w, h);
+
+ ob_debug("ConfigureRequest for \"%s\" desktop %d wmstate %d "
+ "visible %d",
+ client->title,
+ screen_desktop, client->wmstate, client->frame->visible);
+ ob_debug(" x %d y %d w %d h %d b %d",
+ x, y, w, h, client->border_width);
+
+ if (e->xconfigurerequest.value_mask & CWBorderWidth)
+ if (client->border_width != e->xconfigurerequest.border_width) {
+ client->border_width = e->xconfigurerequest.border_width;
+
+ /* if the border width is changing then that is the same
+ as requesting a resize, but we don't actually change
+ the client's border, so it will change their root
+ coordinates (since they include the border width) and
+ we need to a notify then */
+ move = TRUE;
+ }
+
+ if (e->xconfigurerequest.value_mask & CWStackMode) {
+ ObClient *sibling = NULL;
+ gulong ignore_start;
+ gboolean ok = TRUE;
+
+ /* get the sibling */
+ if (e->xconfigurerequest.value_mask & CWSibling) {
+ ObWindow *win;
+ win = window_find(e->xconfigurerequest.above);
+ if (win && WINDOW_IS_CLIENT(win) &&
+ WINDOW_AS_CLIENT(win) != client)
+ {
+ sibling = WINDOW_AS_CLIENT(win);
+ }
+ else
+ /* an invalid sibling was specified so don't restack at
+ all, it won't make sense no matter what we do */
+ ok = FALSE;
+ }
+
+ if (ok) {
+ if (!config_focus_under_mouse)
+ ignore_start = event_start_ignore_all_enters();
+ stacking_restack_request(client, sibling,
+ e->xconfigurerequest.detail);
+ if (!config_focus_under_mouse)
+ event_end_ignore_all_enters(ignore_start);
+ }
+
+ /* a stacking change moves the window without resizing */
+ move = TRUE;
+ }
+
+ if ((e->xconfigurerequest.value_mask & CWX) ||
+ (e->xconfigurerequest.value_mask & CWY) ||
+ (e->xconfigurerequest.value_mask & CWWidth) ||
+ (e->xconfigurerequest.value_mask & CWHeight))
+ {
+ /* don't allow clients to move shaded windows (fvwm does this)
+ */
+ if (e->xconfigurerequest.value_mask & CWX) {
+ if (!client->shaded)
+ x = e->xconfigurerequest.x;
+ move = TRUE;
+ }
+ if (e->xconfigurerequest.value_mask & CWY) {
+ if (!client->shaded)
+ y = e->xconfigurerequest.y;
+ move = TRUE;
+ }
+
+ if (e->xconfigurerequest.value_mask & CWWidth) {
+ w = e->xconfigurerequest.width;
+ resize = TRUE;
+ }
+ if (e->xconfigurerequest.value_mask & CWHeight) {
+ h = e->xconfigurerequest.height;
+ resize = TRUE;
+ }
+ }
+
+ ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d "
+ "move %d resize %d",
+ e->xconfigurerequest.value_mask & CWX, x,
+ e->xconfigurerequest.value_mask & CWY, y,
+ e->xconfigurerequest.value_mask & CWWidth, w,
+ e->xconfigurerequest.value_mask & CWHeight, h,
+ move, resize);
+
+ /* check for broken apps moving to their root position
+
+ XXX remove this some day...that would be nice. right now all
+ kde apps do this when they try activate themselves on another
+ desktop. eg. open amarok window on desktop 1, switch to desktop
+ 2, click amarok tray icon. it will move by its decoration size.
+ */
+ if (x != client->area.x &&
+ x == (client->frame->area.x + client->frame->size.left -
+ (gint)client->border_width) &&
+ y != client->area.y &&
+ y == (client->frame->area.y + client->frame->size.top -
+ (gint)client->border_width) &&
+ w == client->area.width &&
+ h == client->area.height)
+ {
+ ob_debug_type(OB_DEBUG_APP_BUGS,
+ "Application %s is trying to move via "
+ "ConfigureRequest to it's root window position "
+ "but it is not using StaticGravity",
+ client->title);
+ /* don't move it */
+ x = client->area.x;
+ y = client->area.y;
+
+ /* they still requested a move, so don't change whether a
+ notify is sent or not */
+ }
+
+ /* check for broken apps (java swing) moving to 0,0 when there is a
+ strut there.
+
+ XXX remove this some day...that would be nice. but really unexpected
+ from Sun Microsystems.
+ */
+ if (x == 0 && y == 0 && client->gravity == NorthWestGravity &&
+ client_normal(client))
+ {
+ const Rect to = { x, y, w, h };
+
+ /* oldschool fullscreen windows are allowed */
+ if (!client_is_oldfullscreen(client, &to)) {
+ Rect *r;
+
+ r = screen_area(client->desktop, SCREEN_AREA_ALL_MONITORS,
+ NULL);
+ if (r->x || r->y) {
+ /* move the window only to the corner outside struts */
+ x = r->x;
+ y = r->y;
+
+ ob_debug_type(OB_DEBUG_APP_BUGS,
+ "Application %s is trying to move via "
+ "ConfigureRequest to 0,0 using "
+ "NorthWestGravity, while there is a "
+ "strut there. "
+ "Moving buggy app from (0,0) to (%d,%d)",
+ client->title, r->x, r->y);
+ }
+
+ g_slice_free(Rect, r);
+
+ /* they still requested a move, so don't change whether a
+ notify is sent or not */
+ }
+ }
+
+ {
+ gint lw, lh;
+
+ client_try_configure(client, &x, &y, &w, &h, &lw, &lh, FALSE);
+
+ /* if x was not given, then use gravity to figure out the new
+ x. the reference point should not be moved */
+ if ((e->xconfigurerequest.value_mask & CWWidth &&
+ !(e->xconfigurerequest.value_mask & CWX)))
+ client_gravity_resize_w(client, &x, client->area.width, w);
+ /* same for y */
+ if ((e->xconfigurerequest.value_mask & CWHeight &&
+ !(e->xconfigurerequest.value_mask & CWY)))
+ client_gravity_resize_h(client, &y, client->area.height,h);
+
+ client_find_onscreen(client, &x, &y, w, h, FALSE);
+
+ ob_debug("Granting ConfigureRequest x %d y %d w %d h %d",
+ x, y, w, h);
+ client_configure(client, x, y, w, h, FALSE, TRUE, TRUE);
+ }
+ break;
+ }
+ case UnmapNotify:
+ ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
+ "ignores left %d",
+ client->window, e->xunmap.event, e->xunmap.from_configure,
+ client->ignore_unmaps);
+ if (client->ignore_unmaps) {
+ client->ignore_unmaps--;
+ break;
+ }
+ client_unmanage(client);
+ break;
+ case DestroyNotify:
+ ob_debug("DestroyNotify for window 0x%x", client->window);
+ client_unmanage(client);
+ break;
+ case ReparentNotify:
+ /* this is when the client is first taken captive in the frame */
+ if (e->xreparent.parent == client->frame->window) break;
+
+ /*
+ This event is quite rare and is usually handled in unmapHandler.
+ However, if the window is unmapped when the reparent event occurs,
+ the window manager never sees it because an unmap event is not sent
+ to an already unmapped window.
+ */
+
+ ob_debug("ReparentNotify for window 0x%x", client->window);
+ client_unmanage(client);
+ break;
+ case MapRequest:
+ ob_debug("MapRequest for 0x%lx", client->window);
+ if (!client->iconic) break; /* this normally doesn't happen, but if it
+ does, we don't want it!
+ it can happen now when the window is on
+ another desktop, but we still don't
+ want it! */
+ client_activate(client, FALSE, FALSE, TRUE, TRUE, TRUE);
+ break;
+ case ClientMessage:
+ /* validate cuz we query stuff off the client here */
+ if (!client_validate(client)) break;
+
+ if (e->xclient.format != 32) return;
+
+ msgtype = e->xclient.message_type;
+ if (msgtype == OBT_PROP_ATOM(WM_CHANGE_STATE)) {
+ if (!more_client_message_event(client->window, msgtype))
+ client_set_wm_state(client, e->xclient.data.l[0]);
+ } else if (msgtype == OBT_PROP_ATOM(NET_WM_DESKTOP)) {
+ if (!more_client_message_event(client->window, msgtype) &&
+ ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
+ (unsigned)e->xclient.data.l[0] == DESKTOP_ALL))
+ {
+ client_set_desktop(client, (unsigned)e->xclient.data.l[0],
+ FALSE, FALSE);
+ }
+ } else if (msgtype == OBT_PROP_ATOM(NET_WM_STATE)) {
+ gulong ignore_start;
+
+ /* can't compress these */
+ ob_debug("net_wm_state %s %ld %ld for 0x%lx",
+ (e->xclient.data.l[0] == 0 ? "Remove" :
+ e->xclient.data.l[0] == 1 ? "Add" :
+ e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
+ e->xclient.data.l[1], e->xclient.data.l[2],
+ client->window);
+
+ /* ignore enter events caused by these like ob actions do */
+ if (!config_focus_under_mouse)
+ ignore_start = event_start_ignore_all_enters();
+ client_set_state(client, e->xclient.data.l[0],
+ e->xclient.data.l[1], e->xclient.data.l[2]);
+ if (!config_focus_under_mouse)
+ event_end_ignore_all_enters(ignore_start);
+ } else if (msgtype == OBT_PROP_ATOM(NET_CLOSE_WINDOW)) {
+ ob_debug("net_close_window for 0x%lx", client->window);
+ client_close(client);
+ } else if (msgtype == OBT_PROP_ATOM(NET_ACTIVE_WINDOW)) {
+ ob_debug("net_active_window for 0x%lx source=%s",
+ client->window,
+ (e->xclient.data.l[0] == 0 ? "unknown" :
+ (e->xclient.data.l[0] == 1 ? "application" :
+ (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
+ /* XXX make use of data.l[2] !? */
+ if (e->xclient.data.l[0] == 1 || e->xclient.data.l[0] == 2) {
+ /* we can not trust the timestamp from applications.
+ e.g. chromium passes a very old timestamp. openbox thinks
+ the window will get focus and calls XSetInputFocus with the
+ (old) timestamp, which doesn't end up moving focus at all.
+ but the window is raised, not hilited, etc, as if it was
+ really going to get focus.
+
+ so do not use this timestamp in event_curtime, as this would
+ be used in XSetInputFocus.
+ */
+ event_sourcetime = e->xclient.data.l[1];
+ if (e->xclient.data.l[1] == 0)
+ ob_debug_type(OB_DEBUG_APP_BUGS,
+ "_NET_ACTIVE_WINDOW message for window %s is"
+ " missing a timestamp", client->title);
+ } else
+ ob_debug_type(OB_DEBUG_APP_BUGS,
+ "_NET_ACTIVE_WINDOW message for window %s is "
+ "missing source indication", client->title);
+ /* TODO(danakj) This should use
+ (e->xclient.data.l[0] == 0 ||
+ e->xclient.data.l[0] == 2)
+ to determine if a user requested the activation, however GTK+
+ applications seem unable to make this distinction ever
+ (including panels such as xfce4-panel and gnome-panel).
+ So we are left just assuming all activations are from the user.
+ */
+ client_activate(client, FALSE, FALSE, TRUE, TRUE, TRUE);
+ } else if (msgtype == OBT_PROP_ATOM(NET_WM_MOVERESIZE)) {
+ ob_debug("net_wm_moveresize for 0x%lx direction %d",
+ client->window, e->xclient.data.l[2]);
+ if ((Atom)e->xclient.data.l[2] ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
+ (Atom)e->xclient.data.l[2] ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
+ (Atom)e->xclient.data.l[2] ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT) ||
+ (Atom)e->xclient.data.l[2] ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT) ||
+ (Atom)e->xclient.data.l[2] ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT) ||
+ (Atom)e->xclient.data.l[2] ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) ||
+ (Atom)e->xclient.data.l[2] ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM) ||
+ (Atom)e->xclient.data.l[2] ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) ||
+ (Atom)e->xclient.data.l[2] ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
+ (Atom)e->xclient.data.l[2] ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) ||
+ (Atom)e->xclient.data.l[2] ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD) ||
+ (Atom)e->xclient.data.l[2] ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
+ {
+ moveresize_start(client, e->xclient.data.l[0],
+ e->xclient.data.l[1], e->xclient.data.l[3],
+ e->xclient.data.l[2]);
+ }
+ else if ((Atom)e->xclient.data.l[2] ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_CANCEL))
+ moveresize_end(TRUE);
+ } else if (msgtype == OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW)) {
+ gint ograv, x, y, w, h;
+
+ ograv = client->gravity;
+
+ if (e->xclient.data.l[0] & 0xff)
+ client->gravity = e->xclient.data.l[0] & 0xff;
+
+ if (e->xclient.data.l[0] & 1 << 8)
+ x = e->xclient.data.l[1];
+ else
+ x = client->area.x;
+ if (e->xclient.data.l[0] & 1 << 9)
+ y = e->xclient.data.l[2];
+ else
+ y = client->area.y;
+
+ if (e->xclient.data.l[0] & 1 << 10) {
+ w = e->xclient.data.l[3];
+
+ /* if x was not given, then use gravity to figure out the new
+ x. the reference point should not be moved */
+ if (!(e->xclient.data.l[0] & 1 << 8))
+ client_gravity_resize_w(client, &x, client->area.width, w);
+ }
+ else
+ w = client->area.width;
+
+ if (e->xclient.data.l[0] & 1 << 11) {
+ h = e->xclient.data.l[4];
+
+ /* same for y */
+ if (!(e->xclient.data.l[0] & 1 << 9))
+ client_gravity_resize_h(client, &y, client->area.height,h);
+ }
+ else
+ h = client->area.height;
+
+ ob_debug("MOVERESIZE x %d %d y %d %d (gravity %d)",
+ e->xclient.data.l[0] & 1 << 8, x,
+ e->xclient.data.l[0] & 1 << 9, y,
+ client->gravity);
+
+ client_find_onscreen(client, &x, &y, w, h, FALSE);
+
+ client_configure(client, x, y, w, h, FALSE, TRUE, FALSE);
+
+ client->gravity = ograv;
+ } else if (msgtype == OBT_PROP_ATOM(NET_RESTACK_WINDOW)) {
+ if (e->xclient.data.l[0] != 2) {
+ ob_debug_type(OB_DEBUG_APP_BUGS,
+ "_NET_RESTACK_WINDOW sent for window %s with "
+ "invalid source indication %ld",
+ client->title, e->xclient.data.l[0]);
+ } else {
+ ObClient *sibling = NULL;
+ if (e->xclient.data.l[1]) {
+ ObWindow *win = window_find(e->xclient.data.l[1]);
+ if (WINDOW_IS_CLIENT(win) &&
+ WINDOW_AS_CLIENT(win) != client)
+ {
+ sibling = WINDOW_AS_CLIENT(win);
+ }
+ if (sibling == NULL)
+ ob_debug_type(OB_DEBUG_APP_BUGS,
+ "_NET_RESTACK_WINDOW sent for window %s "
+ "with invalid sibling 0x%x",
+ client->title, e->xclient.data.l[1]);
+ }
+ if (e->xclient.data.l[2] == Below ||
+ e->xclient.data.l[2] == BottomIf ||
+ e->xclient.data.l[2] == Above ||
+ e->xclient.data.l[2] == TopIf ||
+ e->xclient.data.l[2] == Opposite)
+ {
+ gulong ignore_start;
+
+ if (!config_focus_under_mouse)
+ ignore_start = event_start_ignore_all_enters();
+ /* just raise, don't activate */
+ stacking_restack_request(client, sibling,
+ e->xclient.data.l[2]);
+ if (!config_focus_under_mouse)
+ event_end_ignore_all_enters(ignore_start);
+
+ /* send a synthetic ConfigureNotify, cuz this is supposed
+ to be like a ConfigureRequest. */
+ client_reconfigure(client, TRUE);
+ } else
+ ob_debug_type(OB_DEBUG_APP_BUGS,
+ "_NET_RESTACK_WINDOW sent for window %s "
+ "with invalid detail %d",
+ client->title, e->xclient.data.l[2]);
+ }
+ }
+ break;
+ case PropertyNotify:
+ /* validate cuz we query stuff off the client here */
+ if (!client_validate(client)) break;
+
+ msgtype = e->xproperty.atom;
+
+ /* ignore changes to some properties if there is another change
+ coming in the queue */
+ {
+ struct ObSkipPropertyChange s;
+ s.window = client->window;
+ s.prop = msgtype;
+ if (xqueue_exists_local(skip_property_change, &s))
+ break;
+ }
+
+ msgtype = e->xproperty.atom;
+ if (msgtype == XA_WM_NORMAL_HINTS) {
+ int x, y, w, h, lw, lh;
+
+ ob_debug("Update NORMAL hints");
+ client_update_normal_hints(client);
+ /* normal hints can make a window non-resizable */
+ client_setup_decor_and_functions(client, FALSE);
+
+ x = client->area.x;
+ y = client->area.y;
+ w = client->area.width;
+ h = client->area.height;
+
+ /* apply the new normal hints */
+ client_try_configure(client, &x, &y, &w, &h, &lw, &lh, FALSE);
+ /* make sure the window is visible, and if the window is resized
+ off-screen due to the normal hints changing then this will push
+ it back onto the screen. */
+ client_find_onscreen(client, &x, &y, w, h, FALSE);
+
+ /* make sure the client's sizes are within its bounds, but don't
+ make it reply with a configurenotify unless something changed.
+ emacs will update its normal hints every time it receives a
+ configurenotify */
+ client_configure(client, x, y, w, h, FALSE, TRUE, FALSE);
+ } else if (msgtype == OBT_PROP_ATOM(MOTIF_WM_HINTS)) {
+ client_get_mwm_hints(client);
+ /* This can override some mwm hints */
+ client_get_type_and_transientness(client);
+
+ /* Apply the changes to the window */
+ client_setup_decor_and_functions(client, TRUE);
+ } else if (msgtype == XA_WM_HINTS) {
+ client_update_wmhints(client);
+ } else if (msgtype == XA_WM_TRANSIENT_FOR) {
+ /* get the transient-ness first, as this affects if the client
+ decides to be transient for the group or not in
+ client_update_transient_for() */
+ client_get_type_and_transientness(client);
+ client_update_transient_for(client);
+ /* type may have changed, so update the layer */
+ client_calc_layer(client);
+ client_setup_decor_and_functions(client, TRUE);
+ } else if (msgtype == OBT_PROP_ATOM(NET_WM_NAME) ||
+ msgtype == OBT_PROP_ATOM(WM_NAME) ||
+ msgtype == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
+ msgtype == OBT_PROP_ATOM(WM_ICON_NAME)) {
+ client_update_title(client);
+ } else if (msgtype == OBT_PROP_ATOM(WM_PROTOCOLS)) {
+ client_update_protocols(client);
+ }
+ else if (msgtype == OBT_PROP_ATOM(NET_WM_STRUT) ||
+ msgtype == OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL)) {
+ client_update_strut(client);
+ }
+ else if (msgtype == OBT_PROP_ATOM(NET_WM_ICON)) {
+ client_update_icons(client);
+ }
+ else if (msgtype == OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY)) {
+ client_update_icon_geometry(client);
+ }
+ else if (msgtype == OBT_PROP_ATOM(NET_WM_USER_TIME)) {
+ guint32 t;
+ if (client == focus_client &&
+ OBT_PROP_GET32(client->window, NET_WM_USER_TIME, CARDINAL, &t)
+ && t && !event_time_after(t, e->xproperty.time) &&
+ (!event_last_user_time ||
+ event_time_after(t, event_last_user_time)))
+ {
+ event_last_user_time = t;
+ }
+ }
+ else if (msgtype == OBT_PROP_ATOM(NET_WM_WINDOW_OPACITY)) {
+ client_update_opacity(client);
+ }
+#ifdef SYNC
+ else if (msgtype == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER)) {
+ /* if they are resizing right now this would cause weird behaviour.
+ if one day a user reports clients stop resizing, then handle
+ this better by resetting a new XSync alarm and stuff on the
+ new counter, but I expect it will never happen */
+ if (moveresize_client == client)
+ moveresize_end(FALSE);
+ client_update_sync_request_counter(client);
+ }
+#endif
+ break;
+ case ColormapNotify:
+ client_update_colormap(client, e->xcolormap.colormap);
+ break;
+ default:
+ ;
+#ifdef SHAPE
+ {
+ int kind;
+ if (obt_display_extension_shape &&
+ e->type == obt_display_extension_shape_basep)
+ {
+ switch (((XShapeEvent*)e)->kind) {
+ case ShapeBounding:
+ case ShapeClip:
+ client->shaped = ((XShapeEvent*)e)->shaped;
+ kind = ShapeBounding;
+ break;
+#ifdef ShapeInput
+ case ShapeInput:
+ client->shaped_input = ((XShapeEvent*)e)->shaped;
+ kind = ShapeInput;
+ break;
+#endif
+ default:
+ g_assert_not_reached();
+ }
+ frame_adjust_shape_kind(client->frame, kind);
+ }
+ }
+#endif
+ }
+}
+
+static void event_handle_dock(ObDock *s, XEvent *e)
+{
+ switch (e->type) {
+ case EnterNotify:
+ dock_hide(FALSE);
+ break;
+ case LeaveNotify:
+ /* don't hide when moving into a dock app */
+ if (e->xcrossing.detail != NotifyInferior)
+ dock_hide(TRUE);
+ break;
+ }
+}
+
+static void event_handle_dockapp(ObDockApp *app, XEvent *e)
+{
+ switch (e->type) {
+ case MotionNotify:
+ dock_app_drag(app, &e->xmotion);
+ break;
+ case UnmapNotify:
+ if (app->ignore_unmaps) {
+ app->ignore_unmaps--;
+ break;
+ }
+ dock_unmanage(app, TRUE);
+ break;
+ case DestroyNotify:
+ case ReparentNotify:
+ dock_unmanage(app, FALSE);
+ break;
+ case ConfigureNotify:
+ dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
+ break;
+ }
+}
+
+static ObMenuFrame* find_active_menu(void)
+{
+ GList *it;
+ ObMenuFrame *ret = NULL;
+
+ for (it = menu_frame_visible; it; it = g_list_next(it)) {
+ ret = it->data;
+ if (ret->selected)
+ break;
+ ret = NULL;
+ }
+ return ret;
+}
+
+static ObMenuFrame* find_active_or_last_menu(void)
+{
+ ObMenuFrame *ret = NULL;
+
+ ret = find_active_menu();
+ if (!ret && menu_frame_visible)
+ ret = menu_frame_visible->data;
+ return ret;
+}
+
+static gboolean event_handle_prompt(ObPrompt *p, XEvent *e)
+{
+ switch (e->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ return prompt_mouse_event(p, e);
+ break;
+ case KeyPress:
+ return prompt_key_event(p, e);
+ break;
+ }
+ return FALSE;
+}
+
+static gboolean event_handle_menu_input(XEvent *ev)
+{
+ gboolean ret = FALSE;
+
+ if (ev->type == ButtonRelease || ev->type == ButtonPress) {
+ ObMenuEntryFrame *e;
+
+ if ((ev->xbutton.button < 4 || ev->xbutton.button > 5) &&
+ ((ev->type == ButtonRelease && menu_hide_delay_reached()) ||
+ ev->type == ButtonPress))
+ {
+ if ((e = menu_entry_frame_under(ev->xbutton.x_root,
+ ev->xbutton.y_root)))
+ {
+ if (ev->type == ButtonPress && e->frame->child)
+ menu_frame_select(e->frame->child, NULL, TRUE);
+ menu_frame_select(e->frame, e, TRUE);
+ if (ev->type == ButtonRelease)
+ menu_entry_frame_execute(e, ev->xbutton.state);
+ }
+ else
+ menu_frame_hide_all();
+ }
+ ret = TRUE;
+ }
+ else if (ev->type == KeyPress || ev->type == KeyRelease) {
+ guint mods;
+ ObMenuFrame *frame;
+
+ /* get the modifiers */
+ mods = obt_keyboard_only_modmasks(ev->xkey.state);
+
+ frame = find_active_or_last_menu();
+ if (frame == NULL)
+ g_assert_not_reached(); /* there is no active menu */
+
+ /* Allow control while going thru the menu */
+ else if (ev->type == KeyPress && (mods & ~ControlMask) == 0) {
+ gunichar unikey;
+ KeySym sym;
+
+ frame->got_press = TRUE;
+ frame->press_keycode = ev->xkey.keycode;
+ frame->press_doexec = FALSE;
+
+ sym = obt_keyboard_keypress_to_keysym(ev);
+
+ if (sym == XK_Escape) {
+ menu_frame_hide_all();
+ ret = TRUE;
+ }
+
+ else if (sym == XK_Left) {
+ /* Left goes to the parent menu */
+ if (frame->parent) {
+ /* remove focus from the child */
+ menu_frame_select(frame, NULL, TRUE);
+ /* and put it in the parent */
+ menu_frame_select(frame->parent, frame->parent->selected,
+ TRUE);
+ }
+ ret = TRUE;
+ }
+
+ else if (sym == XK_Right || sym == XK_Return || sym == XK_KP_Enter)
+ {
+ /* Right and enter goes to the selected submenu.
+ Enter executes instead if it's not on a submenu. */
+
+ if (frame->selected) {
+ const ObMenuEntryType t = frame->selected->entry->type;
+
+ if (t == OB_MENU_ENTRY_TYPE_SUBMENU) {
+ /* make sure it is visible */
+ menu_frame_select(frame, frame->selected, TRUE);
+ /* move focus to the child menu */
+ menu_frame_select_next(frame->child);
+ }
+ else if (sym != XK_Right) {
+ frame->press_doexec = TRUE;
+ }
+ }
+ ret = TRUE;
+ }
+
+ else if (sym == XK_Up) {
+ menu_frame_select_previous(frame);
+ ret = TRUE;
+ }
+
+ else if (sym == XK_Down) {
+ menu_frame_select_next(frame);
+ ret = TRUE;
+ }
+
+ else if (sym == XK_Home) {
+ menu_frame_select_first(frame);
+ ret = TRUE;
+ }
+
+ else if (sym == XK_End) {
+ menu_frame_select_last(frame);
+ ret = TRUE;
+ }
+
+ /* keyboard accelerator shortcuts. (if it was a valid key) */
+ else if (frame->entries &&
+ (unikey =
+ obt_keyboard_keypress_to_unichar(menu_frame_ic(frame),
+ ev)))
+ {
+ GList *start;
+ GList *it;
+ ObMenuEntryFrame *found = NULL;
+ guint num_found = 0;
+
+ /* start after the selected one */
+ start = frame->entries;
+ if (frame->selected) {
+ for (it = start; frame->selected != it->data;
+ it = g_list_next(it))
+ g_assert(it != NULL); /* nothing was selected? */
+ /* next with wraparound */
+ start = g_list_next(it);
+ if (start == NULL) start = frame->entries;
+ }
+
+ it = start;
+ do {
+ ObMenuEntryFrame *e = it->data;
+ gunichar entrykey = 0;
+
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
+ entrykey = e->entry->data.normal.shortcut;
+ else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ entrykey = e->entry->data.submenu.submenu->shortcut;
+
+ if (unikey == entrykey) {
+ if (found == NULL) found = e;
+ ++num_found;
+ }
+
+ /* next with wraparound */
+ it = g_list_next(it);
+ if (it == NULL) it = frame->entries;
+ } while (it != start);
+
+ if (found) {
+ menu_frame_select(frame, found, TRUE);
+
+ if (num_found == 1) {
+ if (found->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
+ /* move focus to the child menu */
+ menu_frame_select_next(frame->child);
+ }
+ else {
+ frame->press_doexec = TRUE;
+ }
+ }
+ ret = TRUE;
+ }
+ }
+ }
+
+ /* Use KeyRelease events for running things so that the key release
+ doesn't get sent to the focused application.
+
+ Allow ControlMask only, and don't bother if the menu is empty */
+ else if (ev->type == KeyRelease && (mods & ~ControlMask) == 0) {
+ if (frame->press_keycode == ev->xkey.keycode &&
+ frame->got_press &&
+ frame->press_doexec)
+ {
+ if (frame->selected)
+ menu_entry_frame_execute(frame->selected, ev->xkey.state);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static gboolean event_look_for_menu_enter(XEvent *ev, gpointer data)
+{
+ const ObMenuFrame *f = (ObMenuFrame*)data;
+ ObMenuEntryFrame *e;
+ return ev->type == EnterNotify &&
+ (e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
+ e->frame == f && !e->ignore_enters;
+}
+
+static void event_handle_menu(ObMenuFrame *frame, XEvent *ev)
+{
+ ObMenuFrame *f;
+ ObMenuEntryFrame *e;
+
+ switch (ev->type) {
+ case EnterNotify:
+ if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.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 LeaveNotify:
+ /*ignore leaves when we're already in the window */
+ if (ev->xcrossing.detail == NotifyInferior)
+ break;
+
+ if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
+ /* check if an EnterNotify event is coming, and if not, then select
+ nothing in the menu */
+ if (!xqueue_exists_local(event_look_for_menu_enter, e->frame))
+ menu_frame_select(e->frame, NULL, FALSE);
+ }
+ break;
+ }
+}
+
+static gboolean event_handle_user_input(ObClient *client, XEvent *e)
+{
+ g_assert(e->type == ButtonPress || e->type == ButtonRelease ||
+ e->type == MotionNotify || e->type == KeyPress ||
+ e->type == KeyRelease);
+
+ if (menu_frame_visible) {
+ if (event_handle_menu_input(e))
+ /* don't use the event if the menu used it, but if the menu
+ didn't use it and it's a keypress that is bound, it will
+ close the menu and be used */
+ return TRUE;
+ }
+
+ /* if the keyboard interactive action uses the event then dont
+ use it for bindings. likewise is moveresize uses the event. */
+ if (actions_interactive_input_event(e) || moveresize_event(e))
+ return TRUE;
+
+ if (moveresize_in_progress)
+ /* make further actions work on the client being
+ moved/resized */
+ client = moveresize_client;
+
+ if (e->type == ButtonPress ||
+ e->type == ButtonRelease ||
+ e->type == MotionNotify)
+ {
+ /* the frame may not be "visible" but they can still click on it
+ in the case where it is animating before disappearing */
+ if (!client || !frame_iconify_animating(client->frame))
+ return mouse_event(client, e);
+ } else
+ return keyboard_event((focus_cycle_target ? focus_cycle_target :
+ (client ? client : focus_client)), e);
+
+ return FALSE;
+}
+
+static void focus_delay_dest(gpointer data)
+{
+ g_slice_free(ObFocusDelayData, data);
+ focus_delay_timeout_id = 0;
+ focus_delay_timeout_client = NULL;
+}
+
+static void unfocus_delay_dest(gpointer data)
+{
+ g_slice_free(ObFocusDelayData, data);
+ unfocus_delay_timeout_id = 0;
+ unfocus_delay_timeout_client = NULL;
+}
+
+static gboolean focus_delay_func(gpointer data)
+{
+ ObFocusDelayData *d = data;
+ Time old = event_curtime; /* save the curtime */
+
+ event_curtime = d->time;
+ event_curserial = d->serial;
+ if (client_focus(d->client) && config_focus_raise)
+ stacking_raise(CLIENT_AS_WINDOW(d->client));
+ event_curtime = old;
+ return FALSE; /* no repeat */
+}
+
+static gboolean unfocus_delay_func(gpointer data)
+{
+ ObFocusDelayData *d = data;
+ Time old = event_curtime; /* save the curtime */
+
+ event_curtime = d->time;
+ event_curserial = d->serial;
+ focus_nothing();
+ event_curtime = old;
+ return FALSE; /* no repeat */
+}
+
+static void focus_delay_client_dest(ObClient *client, gpointer data)
+{
+ if (focus_delay_timeout_client == client && focus_delay_timeout_id)
+ g_source_remove(focus_delay_timeout_id);
+ if (unfocus_delay_timeout_client == client && unfocus_delay_timeout_id)
+ g_source_remove(unfocus_delay_timeout_id);
+}
+
+void event_halt_focus_delay(void)
+{
+ /* ignore all enter events up till the event which caused this to occur */
+ if (event_curserial) event_ignore_enter_range(1, event_curserial);
+ if (focus_delay_timeout_id) g_source_remove(focus_delay_timeout_id);
+ if (unfocus_delay_timeout_id) g_source_remove(unfocus_delay_timeout_id);
+}
+
+gulong event_start_ignore_all_enters(void)
+{
+ return NextRequest(obt_display);
+}
+
+static void event_ignore_enter_range(gulong start, gulong end)
+{
+ ObSerialRange *r;
+
+ g_assert(start != 0);
+ g_assert(end != 0);
+
+ r = g_slice_new(ObSerialRange);
+ r->start = start;
+ r->end = end;
+ ignore_serials = g_slist_prepend(ignore_serials, r);
+
+ ob_debug_type(OB_DEBUG_FOCUS, "ignoring enters from %lu until %lu",
+ r->start, r->end);
+
+ /* increment the serial so we don't ignore events we weren't meant to */
+ OBT_PROP_ERASE(screen_support_win, MOTIF_WM_HINTS);
+}
+
+void event_end_ignore_all_enters(gulong start)
+{
+ /* Use (NextRequest-1) so that we ignore up to the current serial only.
+ Inside event_ignore_enter_range, we increment the serial by one, but if
+ we ignore that serial too, then any enter events generated by mouse
+ movement will be ignored until we create some further network traffic.
+ Instead ignore up to NextRequest-1, then when we increment the serial,
+ we will be *past* the range of ignored serials */
+ event_ignore_enter_range(start, NextRequest(obt_display)-1);
+}
+
+static gboolean is_enter_focus_event_ignored(gulong serial)
+{
+ GSList *it, *next;
+
+ for (it = ignore_serials; it; it = next) {
+ ObSerialRange *r = it->data;
+
+ next = g_slist_next(it);
+
+ if ((glong)(serial - r->end) > 0) {
+ /* past the end */
+ ignore_serials = g_slist_delete_link(ignore_serials, it);
+ g_slice_free(ObSerialRange, r);
+ }
+ else if ((glong)(serial - r->start) >= 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void event_cancel_all_key_grabs(void)
+{
+ if (actions_interactive_act_running()) {
+ actions_interactive_cancel_act();
+ ob_debug("KILLED interactive action");
+ }
+ else if (menu_frame_visible) {
+ menu_frame_hide_all();
+ ob_debug("KILLED open menus");
+ }
+ else if (moveresize_in_progress) {
+ moveresize_end(TRUE);
+ ob_debug("KILLED interactive moveresize");
+ }
+ else if (grab_on_keyboard()) {
+ ungrab_keyboard();
+ ob_debug("KILLED active grab on keyboard");
+ }
+ else
+ ungrab_passive_key();
+
+ XSync(obt_display, FALSE);
+}
+
+gboolean event_time_after(guint32 t1, guint32 t2)
+{
+ g_assert(t1 != CurrentTime);
+ g_assert(t2 != CurrentTime);
+
+ /*
+ Timestamp values wrap around (after about 49.7 days). The server, given
+ its current time is represented by timestamp T, always interprets
+ timestamps from clients by treating half of the timestamp space as being
+ later in time than T.
+ - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
+ */
+
+ /* TIME_HALF is not half of the number space of a Time type variable.
+ * Rather, it is half the number space of a timestamp value, which is
+ * always 32 bits. */
+#define TIME_HALF (guint32)(1 << 31)
+
+ if (t2 >= TIME_HALF)
+ /* t2 is in the second half so t1 might wrap around and be smaller than
+ t2 */
+ return t1 >= t2 || t1 < (t2 + TIME_HALF);
+ else
+ /* t2 is in the first half so t1 has to come after it */
+ return t1 >= t2 && t1 < (t2 + TIME_HALF);
+}
+
+gboolean find_timestamp(XEvent *e, gpointer data)
+{
+ const Time t = event_get_timestamp(e);
+ if (t && t >= event_curtime) {
+ event_curtime = t;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static Time next_time(void)
+{
+ /* Some events don't come with timestamps :(
+ ...but we can get one anyways >:) */
+
+ /* Generate a timestamp so there is guaranteed at least one in the queue
+ eventually */
+ XChangeProperty(obt_display, screen_support_win,
+ OBT_PROP_ATOM(WM_CLASS), OBT_PROP_ATOM(STRING),
+ 8, PropModeAppend, NULL, 0);
+
+ /* Grab the first timestamp available */
+ xqueue_exists(find_timestamp, NULL);
+
+ /*g_assert(event_curtime != CurrentTime);*/
+
+ /* Save the time so we don't have to do this again for this event */
+ return event_curtime;
+}
+
+Time event_time(void)
+{
+ if (event_curtime) return event_curtime;
+
+ return next_time();
+}
+
+Time event_source_time(void)
+{
+ return event_sourcetime;
+}
+
+void event_reset_time(void)
+{
+ next_time();
+}
+
+void event_update_user_time(void)
+{
+ event_last_user_time = event_time();
+}
+
+void event_reset_user_time(void)
+{
+ event_last_user_time = CurrentTime;
+}
diff --git a/openbox/event.h b/openbox/event.h
new file mode 100644
index 0000000..4d9984e
--- /dev/null
+++ b/openbox/event.h
@@ -0,0 +1,91 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ event.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __events_h
+#define __events_h
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+struct _ObClient;
+
+/*! 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 */
+
+/*! The last user-interaction time, as given by the clients */
+extern Time event_last_user_time;
+
+void event_startup(gboolean reconfig);
+void event_shutdown(gboolean reconfig);
+
+/*! Make as if the mouse just entered the client, use only when using focus
+ follows mouse */
+void event_enter_client(struct _ObClient *client);
+
+/*! Make as if the mouse just left the client, use only when using focus
+ follows mouse */
+void event_leave_client(struct _ObClient *client);
+
+/*! Make mouse focus not move at all from the stuff that happens between these
+ two function calls. */
+gulong event_start_ignore_all_enters(void);
+void event_end_ignore_all_enters(gulong start);
+
+/*! End *all* active and passive grabs on the keyboard
+ This is called in situations where if there is a grab going on, then
+ we need to cancel it. If we move focus during the grab, applications
+ will get NotifyWhileGrabbed events and ignore them!
+
+ Actions should not rely on being able to move focus during an
+ interactive grab. */
+void event_cancel_all_key_grabs(void);
+
+/* Halts any focus delay in progress, use this when the user is selecting a
+ window for focus */
+void event_halt_focus_delay(void);
+
+/*! Compare t1 and t2, taking into account wraparound. True if t1
+ comes at the same time or later than t2. */
+gboolean event_time_after(guint32 t1, guint32 t2);
+
+/*! Time at which the current event occured. If this is not known, this
+ is a time at or after it, but at or before any other events we will process
+*/
+Time event_time(void);
+
+/*! Force event_time() to skip the current timestamp and look for the next
+ one. */
+void event_reset_time(void);
+
+/*! A time at which an event happened that caused this current event to be
+ generated. This is a user-provided time and not to be trusted.
+ Returns CurrentTime if there was no source time provided.
+*/
+Time event_source_time(void);
+
+/*! Update the timestamp for when the user has last used the focused window.
+ This updates the timestamp to the time of the last event, given by
+ event_time().
+*/
+void event_update_user_time(void);
+
+/*! Reset the timestamp for when the user has last used the focused window. */
+void event_reset_user_time(void);
+
+#endif
diff --git a/openbox/focus.c b/openbox/focus.c
new file mode 100644
index 0000000..a4626bf
--- /dev/null
+++ b/openbox/focus.c
@@ -0,0 +1,389 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ focus.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "debug.h"
+#include "event.h"
+#include "openbox.h"
+#include "grab.h"
+#include "client.h"
+#include "config.h"
+#include "group.h"
+#include "focus_cycle.h"
+#include "screen.h"
+#include "keyboard.h"
+#include "focus.h"
+#include "stacking.h"
+#include "obt/prop.h"
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+#define FOCUS_INDICATOR_WIDTH 6
+
+ObClient *focus_client = NULL;
+GList *focus_order = NULL;
+
+void focus_startup(gboolean reconfig)
+{
+ if (reconfig) return;
+
+ /* start with nothing focused */
+ focus_nothing();
+}
+
+void focus_shutdown(gboolean reconfig)
+{
+ if (reconfig) return;
+
+ /* reset focus to root */
+ XSetInputFocus(obt_display, PointerRoot, RevertToNone, CurrentTime);
+}
+
+static void push_to_top(ObClient *client)
+{
+ ObClient *p;
+
+ /* if it is modal for a single window, then put that window at the top
+ of the focus order first, so it will be right after ours. the same is
+ done with stacking */
+ if (client->modal && (p = client_direct_parent(client)))
+ push_to_top(p);
+
+ focus_order = g_list_remove(focus_order, client);
+ focus_order = g_list_prepend(focus_order, client);
+}
+
+void focus_set_client(ObClient *client)
+{
+ Window active;
+
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "focus_set_client 0x%lx", client ? client->window : 0);
+
+ if (focus_client == client)
+ return;
+
+ /* uninstall the old colormap, and install the new one */
+ screen_install_colormap(focus_client, FALSE);
+ screen_install_colormap(client, TRUE);
+
+ focus_client = client;
+
+ if (client != NULL) {
+ /* move to the top of the list */
+ push_to_top(client);
+ /* remove hiliting from the window when it gets focused */
+ client_hilite(client, FALSE);
+
+ /* make sure the focus cycle popup shows things in the right order */
+ focus_cycle_reorder();
+ }
+
+ /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
+ if (ob_state() != OB_STATE_EXITING) {
+ active = client ? client->window : None;
+ OBT_PROP_SET32(obt_root(ob_screen), NET_ACTIVE_WINDOW, WINDOW, active);
+ }
+
+ /* when focus is moved to a new window, the last_user_time timestamp would
+ no longer be valid, as it applies for the focused window */
+ event_reset_user_time();
+}
+
+static ObClient* focus_fallback_target(gboolean allow_refocus,
+ gboolean allow_pointer,
+ gboolean allow_omnipresent,
+ ObClient *old)
+{
+ GList *it;
+ ObClient *c;
+
+ ob_debug_type(OB_DEBUG_FOCUS, "trying pointer stuff");
+ if (allow_pointer && config_focus_follow)
+ if ((c = client_under_pointer()) &&
+ (allow_refocus || client_focus_target(c) != old) &&
+ (client_normal(c) &&
+ client_focus(c)))
+ {
+ ob_debug_type(OB_DEBUG_FOCUS, "found in pointer stuff");
+ return c;
+ }
+
+ ob_debug_type(OB_DEBUG_FOCUS, "trying the focus order");
+ for (it = focus_order; it; it = g_list_next(it)) {
+ c = it->data;
+ /* fallback focus to a window if:
+ 1. it is on the current desktop. this ignores omnipresent
+ windows, which are problematic in their own rite, unless they are
+ specifically allowed
+ 2. it is a valid auto-focus target
+ 3. it is not shaded
+ */
+ if ((allow_omnipresent || c->desktop == screen_desktop) &&
+ focus_valid_target(c, screen_desktop,
+ TRUE, FALSE, FALSE, TRUE, FALSE, FALSE,
+ FALSE) &&
+ !c->shaded &&
+ (allow_refocus || client_focus_target(c) != old) &&
+ client_focus(c))
+ {
+ ob_debug_type(OB_DEBUG_FOCUS, "found in focus order");
+ return c;
+ }
+ }
+
+ ob_debug_type(OB_DEBUG_FOCUS, "trying a desktop window");
+ for (it = focus_order; it; it = g_list_next(it)) {
+ c = it->data;
+ /* fallback focus to a window if:
+ 1. it is on the current desktop. this ignores omnipresent
+ windows, which are problematic in their own rite.
+ 2. it is a normal type window, don't fall back onto a dock or
+ a splashscreen or a desktop window (save the desktop as a
+ backup fallback though)
+ */
+ if (focus_valid_target(c, screen_desktop,
+ TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE) &&
+ (allow_refocus || client_focus_target(c) != old) &&
+ client_focus(c))
+ {
+ ob_debug_type(OB_DEBUG_FOCUS, "found a desktop window");
+ return c;
+ }
+ }
+
+ return NULL;
+}
+
+ObClient* focus_fallback(gboolean allow_refocus, gboolean allow_pointer,
+ gboolean allow_omnipresent, gboolean focus_lost)
+{
+ ObClient *new;
+ ObClient *old = focus_client;
+
+ /* unfocus any focused clients.. they can be focused by Pointer events
+ and such, and then when we try focus them, we won't get a FocusIn
+ event at all for them. */
+ if (focus_lost)
+ focus_nothing();
+
+ new = focus_fallback_target(allow_refocus, allow_pointer,
+ allow_omnipresent, old);
+ /* get what was really focused */
+ if (new) new = client_focus_target(new);
+
+ return new;
+}
+
+void focus_nothing(void)
+{
+ /* nothing is focused, update the colormap and _the root property_ */
+ focus_set_client(NULL);
+
+ /* when nothing will be focused, send focus to the backup target */
+ XSetInputFocus(obt_display, screen_support_win, RevertToPointerRoot,
+ event_time());
+}
+
+void focus_order_add_new(ObClient *c)
+{
+ if (c->iconic)
+ focus_order_to_top(c);
+ else {
+ g_assert(!g_list_find(focus_order, c));
+ /* if there are only iconic windows, put this above them in the order,
+ but if there are not, then put it under the currently focused one */
+ if (focus_order && ((ObClient*)focus_order->data)->iconic)
+ focus_order = g_list_insert(focus_order, c, 0);
+ else
+ focus_order = g_list_insert(focus_order, c, 1);
+ }
+
+ focus_cycle_addremove(c, TRUE);
+}
+
+void focus_order_remove(ObClient *c)
+{
+ focus_order = g_list_remove(focus_order, c);
+
+ focus_cycle_addremove(c, TRUE);
+}
+
+void focus_order_like_new(struct _ObClient *c)
+{
+ focus_order = g_list_remove(focus_order, c);
+ focus_order_add_new(c);
+}
+
+void focus_order_to_top(ObClient *c)
+{
+ focus_order = g_list_remove(focus_order, c);
+ if (!c->iconic) {
+ focus_order = g_list_prepend(focus_order, c);
+ } else {
+ GList *it;
+
+ /* insert before first iconic window */
+ for (it = focus_order;
+ it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
+ focus_order = g_list_insert_before(focus_order, it, c);
+ }
+
+ focus_cycle_reorder();
+}
+
+void focus_order_to_bottom(ObClient *c)
+{
+ focus_order = g_list_remove(focus_order, c);
+ if (c->iconic) {
+ focus_order = g_list_append(focus_order, c);
+ } else {
+ GList *it;
+
+ /* insert before first iconic window */
+ for (it = focus_order;
+ it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
+ focus_order = g_list_insert_before(focus_order, it, c);
+ }
+
+ focus_cycle_reorder();
+}
+
+ObClient *focus_order_find_first(guint desktop)
+{
+ GList *it;
+ for (it = focus_order; it; it = g_list_next(it)) {
+ ObClient *c = it->data;
+ if (c->desktop == desktop || c->desktop == DESKTOP_ALL)
+ return c;
+ }
+ return NULL;
+}
+
+/*! Returns if a focus target has valid group siblings that can be cycled
+ to in its place */
+static gboolean focus_target_has_siblings(ObClient *ft,
+ gboolean iconic_windows,
+ gboolean all_desktops)
+
+{
+ GSList *it;
+
+ if (!ft->group) return FALSE;
+
+ for (it = ft->group->members; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+ /* check that it's not a helper window to avoid infinite recursion */
+ if (c != ft && c->type == OB_CLIENT_TYPE_NORMAL &&
+ focus_valid_target(c, screen_desktop,
+ TRUE, iconic_windows, all_desktops,
+ TRUE, FALSE, FALSE, FALSE))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+gboolean focus_valid_target(ObClient *ft,
+ guint desktop,
+ gboolean helper_windows,
+ gboolean iconic_windows,
+ gboolean all_desktops,
+ gboolean nonhilite_windows,
+ gboolean dock_windows,
+ gboolean desktop_windows,
+ gboolean user_request)
+{
+ /* NOTE: if any of these things change on a client, then they should call
+ focus_cycle_addremove() to make sure the client is not shown/hidden
+ when it should not be */
+
+ gboolean ok = FALSE;
+
+ /* see if the window is still managed or is going away */
+ if (!ft->managed) return FALSE;
+
+ /* it's on this desktop unless you want all desktops.
+
+ do this check first because it will usually filter out the most
+ windows */
+ ok = (all_desktops || ft->desktop == desktop ||
+ ft->desktop == DESKTOP_ALL);
+
+ /* if we only include hilited windows, check if the window is */
+ ok = ok && (nonhilite_windows || ft->demands_attention);
+
+ /* the window can receive focus somehow */
+ ok = ok && (ft->can_focus || ft->focus_notify);
+
+ /* the window is not iconic, or we're allowed to go to iconic ones */
+ ok = ok && (iconic_windows || !ft->iconic);
+
+ /* it's the right type of window */
+ if (dock_windows || desktop_windows)
+ ok = ok && ((dock_windows && ft->type == OB_CLIENT_TYPE_DOCK) ||
+ (desktop_windows && ft->type == OB_CLIENT_TYPE_DESKTOP));
+ /* modal windows are important and can always get focus if they are
+ visible and stuff, so don't change 'ok' based on their type */
+ else if (!ft->modal)
+ /* normal non-helper windows are valid targets */
+ ok = ok &&
+ ((client_normal(ft) && !client_helper(ft))
+ ||
+ /* helper windows are valid targets if... */
+ (client_helper(ft) &&
+ /* ...a window in its group already has focus and we want to
+ include helper windows ... */
+ ((focus_client && ft->group == focus_client->group &&
+ helper_windows) ||
+ /* ... or if there are no other windows in its group
+ that can be focused instead */
+ !focus_target_has_siblings(ft, iconic_windows, all_desktops))));
+
+ /* it's not set to skip the taskbar (but this is overridden if the
+ window is modal or if the user asked for this window to be focused,
+ or if the window is iconified (and does not have any parents with
+ which to uniconify it), and it is not used for windows which are
+ hilited, or dialog windows as these need user interaction and should
+ not be long-lasting windows */
+ ok = ok && (!ft->skip_taskbar ||
+ (ft->modal || user_request ||
+ (ft->iconic && !ft->parents) ||
+ ft->demands_attention ||
+ ft->type == OB_CLIENT_TYPE_DIALOG));
+
+ /* it's not going to just send focus off somewhere else (modal window),
+ unless that modal window is not one of our valid targets, then let
+ you choose this window and bring the modal one here */
+ {
+ ObClient *cft = client_focus_target(ft);
+ ok = ok && (ft == cft || !focus_valid_target(cft,
+ screen_desktop,
+ TRUE,
+ iconic_windows,
+ all_desktops,
+ nonhilite_windows,
+ dock_windows,
+ desktop_windows,
+ FALSE));
+ }
+
+ return ok;
+}
diff --git a/openbox/focus.h b/openbox/focus.h
new file mode 100644
index 0000000..1fc1eb5
--- /dev/null
+++ b/openbox/focus.h
@@ -0,0 +1,81 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ focus.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __focus_h
+#define __focus_h
+
+#include "misc.h"
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+struct _ObClient;
+
+/*! The client which is currently focused */
+extern struct _ObClient *focus_client;
+
+/*! The recent focus order on each desktop */
+extern GList *focus_order;
+
+void focus_startup(gboolean reconfig);
+void focus_shutdown(gboolean reconfig);
+
+/*! Specify which client is currently focused, this doesn't actually
+ send focus anywhere, its called by the Focus event handlers */
+void focus_set_client(struct _ObClient *client);
+
+/*! Focus nothing, but let keyboard events be caught. */
+void focus_nothing(void);
+
+/*! Call this when you need to focus something! */
+struct _ObClient* focus_fallback(gboolean allow_refocus,
+ gboolean allow_pointer,
+ gboolean allow_omnipresent,
+ gboolean focus_lost);
+
+/*! Add a new client into the focus order */
+void focus_order_add_new(struct _ObClient *c);
+
+/*! Remove a client from the focus order */
+void focus_order_remove(struct _ObClient *c);
+
+/*! Move a client to the top of the focus order */
+void focus_order_to_top(struct _ObClient *c);
+
+/*! Move a client to where it would be if it was newly added to the focus order
+ */
+void focus_order_like_new(struct _ObClient *c);
+
+/*! Move a client to the bottom of the focus order (keeps iconic windows at the
+ very bottom always though). */
+void focus_order_to_bottom(struct _ObClient *c);
+
+struct _ObClient *focus_order_find_first(guint desktop);
+
+gboolean focus_valid_target(struct _ObClient *ft,
+ guint desktop,
+ gboolean helper_windows,
+ gboolean iconic_windows,
+ gboolean all_desktops,
+ gboolean nonhilite_windows,
+ gboolean dock_windows,
+ gboolean desktop_windows,
+ gboolean user_request);
+
+#endif
diff --git a/openbox/focus_cycle.c b/openbox/focus_cycle.c
new file mode 100644
index 0000000..6d4cc2a
--- /dev/null
+++ b/openbox/focus_cycle.c
@@ -0,0 +1,364 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ focus_cycle.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "focus_cycle.h"
+#include "focus_cycle_indicator.h"
+#include "client.h"
+#include "frame.h"
+#include "focus.h"
+#include "screen.h"
+#include "openbox.h"
+#include "debug.h"
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+typedef enum {
+ OB_CYCLE_NONE = 0,
+ OB_CYCLE_NORMAL,
+ OB_CYCLE_DIRECTIONAL
+} ObCycleType;
+
+ObClient *focus_cycle_target = NULL;
+static ObCycleType focus_cycle_type = OB_CYCLE_NONE;
+static gboolean focus_cycle_linear;
+static gboolean focus_cycle_iconic_windows;
+static gboolean focus_cycle_all_desktops;
+static gboolean focus_cycle_nonhilite_windows;
+static gboolean focus_cycle_dock_windows;
+static gboolean focus_cycle_desktop_windows;
+
+static ObClient *focus_find_directional(ObClient *c,
+ ObDirection dir,
+ gboolean dock_windows,
+ gboolean desktop_windows);
+
+void focus_cycle_startup(gboolean reconfig)
+{
+ if (reconfig) return;
+}
+
+void focus_cycle_shutdown(gboolean reconfig)
+{
+ if (reconfig) return;
+}
+
+void focus_cycle_addremove(ObClient *c, gboolean redraw)
+{
+ if (!focus_cycle_type)
+ return;
+
+ if (focus_cycle_type == OB_CYCLE_DIRECTIONAL) {
+ if (c && focus_cycle_target == c) {
+ focus_directional_cycle(0, TRUE, TRUE, TRUE, TRUE,
+ TRUE, TRUE, TRUE);
+ }
+ }
+ else if (c && redraw) {
+ gboolean v, s;
+
+ v = focus_cycle_valid(c);
+ s = focus_cycle_popup_is_showing(c);
+
+ if (v != s)
+ focus_cycle_reorder();
+ }
+ else if (redraw) {
+ focus_cycle_reorder();
+ }
+}
+
+void focus_cycle_reorder()
+{
+ if (focus_cycle_type == OB_CYCLE_NORMAL) {
+ focus_cycle_target = focus_cycle_popup_refresh(focus_cycle_target,
+ TRUE,
+ focus_cycle_linear);
+ 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, 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 done, gboolean cancel)
+{
+ static GList *order = NULL;
+ GList *it, *start, *list;
+ ObClient *ft = NULL;
+ ObClient *ret = NULL;
+
+ if (interactive) {
+ if (cancel) {
+ focus_cycle_target = NULL;
+ goto done_cycle;
+ } else if (done)
+ 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 (focus_cycle_target == NULL) {
+ focus_cycle_linear = linear;
+ focus_cycle_iconic_windows = TRUE;
+ focus_cycle_all_desktops = all_desktops;
+ focus_cycle_nonhilite_windows = nonhilite_windows;
+ focus_cycle_dock_windows = dock_windows;
+ focus_cycle_desktop_windows = desktop_windows;
+ start = it = g_list_find(list, focus_client);
+ } else
+ start = it = g_list_find(list, focus_cycle_target);
+
+ if (!start) /* switched desktops or something? */
+ start = it = forward ? g_list_last(list) : g_list_first(list);
+ if (!start) goto done_cycle;
+
+ do {
+ if (forward) {
+ it = it->next;
+ if (it == NULL) it = g_list_first(list);
+ } else {
+ it = it->prev;
+ if (it == NULL) it = g_list_last(list);
+ }
+ 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) {
+ focus_cycle_target = ft;
+ focus_cycle_type = OB_CYCLE_NORMAL;
+ done = TRUE;
+ break;
+ }
+ }
+ } while (it != start);
+
+done_cycle:
+ if (done && !cancel) ret = focus_cycle_target;
+
+ focus_cycle_target = NULL;
+ focus_cycle_type = OB_CYCLE_NONE;
+ g_list_free(order);
+ order = NULL;
+
+ if (interactive) {
+ focus_cycle_draw_indicator(NULL);
+ focus_cycle_popup_hide();
+ }
+
+ return ret;
+}
+
+/* this be mostly ripped from fvwm */
+static ObClient *focus_find_directional(ObClient *c, ObDirection dir,
+ gboolean dock_windows,
+ gboolean desktop_windows)
+{
+ gint my_cx, my_cy, his_cx, his_cy;
+ gint offset = 0;
+ gint distance = 0;
+ gint score, best_score;
+ ObClient *best_client, *cur;
+ GList *it;
+
+ if (!client_list)
+ return NULL;
+
+ /* first, find the centre coords of the currently focused window */
+ my_cx = c->frame->area.x + c->frame->area.width / 2;
+ my_cy = c->frame->area.y + c->frame->area.height / 2;
+
+ best_score = -1;
+ best_client = c;
+
+ for (it = g_list_first(client_list); it; it = g_list_next(it)) {
+ cur = it->data;
+
+ /* the currently selected window isn't interesting */
+ if (cur == c)
+ continue;
+ if (!focus_cycle_valid(it->data))
+ continue;
+
+ /* find the centre coords of this window, from the
+ * currently focused window's point of view */
+ his_cx = (cur->frame->area.x - my_cx)
+ + cur->frame->area.width / 2;
+ his_cy = (cur->frame->area.y - my_cy)
+ + cur->frame->area.height / 2;
+
+ if (dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
+ dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST)
+ {
+ gint tx;
+ /* Rotate the diagonals 45 degrees counterclockwise.
+ * To do this, multiply the matrix /+h +h\ with the
+ * vector (x y). \-h +h/
+ * h = sqrt(0.5). We can set h := 1 since absolute
+ * distance doesn't matter here. */
+ tx = his_cx + his_cy;
+ his_cy = -his_cx + his_cy;
+ his_cx = tx;
+ }
+
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ case OB_DIRECTION_SOUTH:
+ case OB_DIRECTION_NORTHEAST:
+ case OB_DIRECTION_SOUTHWEST:
+ offset = (his_cx < 0) ? -his_cx : his_cx;
+ distance = ((dir == OB_DIRECTION_NORTH ||
+ dir == OB_DIRECTION_NORTHEAST) ?
+ -his_cy : his_cy);
+ break;
+ case OB_DIRECTION_EAST:
+ case OB_DIRECTION_WEST:
+ case OB_DIRECTION_SOUTHEAST:
+ case OB_DIRECTION_NORTHWEST:
+ offset = (his_cy < 0) ? -his_cy : his_cy;
+ distance = ((dir == OB_DIRECTION_WEST ||
+ dir == OB_DIRECTION_NORTHWEST) ?
+ -his_cx : his_cx);
+ break;
+ }
+
+ /* the target must be in the requested direction */
+ if (distance <= 0)
+ continue;
+
+ /* Calculate score for this window. The smaller the better. */
+ score = distance + offset;
+
+ /* windows more than 45 degrees off the direction are
+ * heavily penalized and will only be chosen if nothing
+ * else within a million pixels */
+ if (offset > distance)
+ score += 1000000;
+
+ if (best_score == -1 || score < best_score) {
+ best_client = cur;
+ best_score = score;
+ }
+ }
+
+ return best_client;
+}
+
+ObClient* focus_directional_cycle(ObDirection dir, gboolean dock_windows,
+ gboolean desktop_windows,
+ gboolean interactive,
+ gboolean showbar, gboolean dialog,
+ gboolean done, gboolean cancel)
+{
+ static ObClient *first = NULL;
+ ObClient *ft = NULL;
+ ObClient *ret = NULL;
+
+ if (cancel) {
+ focus_cycle_target = NULL;
+ goto done_cycle;
+ } else if (done && interactive)
+ goto done_cycle;
+
+ if (!focus_order)
+ goto done_cycle;
+
+ if (focus_cycle_target == NULL) {
+ focus_cycle_linear = FALSE;
+ focus_cycle_iconic_windows = FALSE;
+ focus_cycle_all_desktops = FALSE;
+ focus_cycle_nonhilite_windows = TRUE;
+ focus_cycle_dock_windows = dock_windows;
+ focus_cycle_desktop_windows = desktop_windows;
+ }
+
+ if (!first) first = focus_client;
+
+ if (focus_cycle_target)
+ ft = focus_find_directional(focus_cycle_target, dir, dock_windows,
+ desktop_windows);
+ else if (first)
+ ft = focus_find_directional(first, dir, dock_windows, desktop_windows);
+ else {
+ GList *it;
+
+ for (it = focus_order; it; it = g_list_next(it))
+ if (focus_cycle_valid(it->data)) {
+ ft = it->data;
+ break;
+ }
+ }
+
+ if (ft && ft != focus_cycle_target) {/* prevents flicker */
+ focus_cycle_target = ft;
+ focus_cycle_type = OB_CYCLE_DIRECTIONAL;
+ if (!interactive)
+ goto done_cycle;
+ focus_cycle_draw_indicator(showbar ? ft : NULL);
+ }
+ if (focus_cycle_target && dialog)
+ /* same arguments as focus_target_valid */
+ focus_cycle_popup_single_show(focus_cycle_target);
+ return focus_cycle_target;
+
+done_cycle:
+ if (done && !cancel) ret = focus_cycle_target;
+
+ first = NULL;
+ focus_cycle_target = NULL;
+ focus_cycle_type = OB_CYCLE_NONE;
+
+ focus_cycle_draw_indicator(NULL);
+ focus_cycle_popup_single_hide();
+
+ return ret;
+}
+
+gboolean focus_cycle_valid(struct _ObClient *client)
+{
+ return focus_valid_target(client, screen_desktop, TRUE,
+ focus_cycle_iconic_windows,
+ focus_cycle_all_desktops,
+ focus_cycle_nonhilite_windows,
+ focus_cycle_dock_windows,
+ focus_cycle_desktop_windows,
+ FALSE);
+}
diff --git a/openbox/focus_cycle.h b/openbox/focus_cycle.h
new file mode 100644
index 0000000..9394b3d
--- /dev/null
+++ b/openbox/focus_cycle.h
@@ -0,0 +1,58 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ focus_cycle.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __focus_cycle_h
+#define __focus_cycle_h
+
+#include "misc.h"
+#include "focus_cycle_popup.h"
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+struct _ObClient;
+
+/*! The client which appears focused during a focus cycle operation */
+extern struct _ObClient *focus_cycle_target;
+
+void focus_cycle_startup(gboolean reconfig);
+void focus_cycle_shutdown(gboolean reconfig);
+
+/*! Cycle focus amongst windows. */
+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 done, gboolean cancel);
+struct _ObClient* focus_directional_cycle(ObDirection dir,
+ gboolean dock_windows,
+ gboolean desktop_windows,
+ gboolean interactive,
+ gboolean showbar,
+ gboolean dialog,
+ gboolean done, gboolean cancel);
+
+/*! Set @redraw to FALSE if there are more clients to be added/removed first */
+void focus_cycle_addremove(struct _ObClient *ifclient, gboolean redraw);
+void focus_cycle_reorder();
+
+gboolean focus_cycle_valid(struct _ObClient *client);
+
+#endif
diff --git a/openbox/focus_cycle_indicator.c b/openbox/focus_cycle_indicator.c
new file mode 100644
index 0000000..8952790
--- /dev/null
+++ b/openbox/focus_cycle_indicator.c
@@ -0,0 +1,287 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ focus_cycle_indicator.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "focus_cycle.h"
+#include "focus_cycle_indicator.h"
+#include "client.h"
+#include "openbox.h"
+#include "frame.h"
+#include "event.h"
+#include "obrender/render.h"
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+#define FOCUS_INDICATOR_WIDTH 6
+
+static struct
+{
+ ObInternalWindow top;
+ ObInternalWindow left;
+ ObInternalWindow right;
+ ObInternalWindow bottom;
+} focus_indicator;
+
+static RrAppearance *a_focus_indicator;
+static RrColor *color_white;
+static gboolean visible;
+
+static Window create_window(Window parent, gulong mask,
+ XSetWindowAttributes *attrib)
+{
+ return XCreateWindow(obt_display, parent, 0, 0, 1, 1, 0,
+ RrDepth(ob_rr_inst), InputOutput,
+ RrVisual(ob_rr_inst), mask, attrib);
+
+}
+
+void focus_cycle_indicator_startup(gboolean reconfig)
+{
+ XSetWindowAttributes attr;
+
+ visible = FALSE;
+
+ if (reconfig) return;
+
+ focus_indicator.top.type = OB_WINDOW_CLASS_INTERNAL;
+ focus_indicator.left.type = OB_WINDOW_CLASS_INTERNAL;
+ focus_indicator.right.type = OB_WINDOW_CLASS_INTERNAL;
+ focus_indicator.bottom.type = OB_WINDOW_CLASS_INTERNAL;
+
+ attr.override_redirect = True;
+ attr.background_pixel = BlackPixel(obt_display, ob_screen);
+ focus_indicator.top.window =
+ create_window(obt_root(ob_screen),
+ CWOverrideRedirect | CWBackPixel, &attr);
+ focus_indicator.left.window =
+ create_window(obt_root(ob_screen),
+ CWOverrideRedirect | CWBackPixel, &attr);
+ focus_indicator.right.window =
+ create_window(obt_root(ob_screen),
+ CWOverrideRedirect | CWBackPixel, &attr);
+ focus_indicator.bottom.window =
+ create_window(obt_root(ob_screen),
+ CWOverrideRedirect | CWBackPixel, &attr);
+
+ stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.top));
+ stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.left));
+ stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.right));
+ stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.bottom));
+ window_add(&focus_indicator.top.window,
+ INTERNAL_AS_WINDOW(&focus_indicator.top));
+ window_add(&focus_indicator.left.window,
+ INTERNAL_AS_WINDOW(&focus_indicator.left));
+ window_add(&focus_indicator.right.window,
+ INTERNAL_AS_WINDOW(&focus_indicator.right));
+ window_add(&focus_indicator.bottom.window,
+ INTERNAL_AS_WINDOW(&focus_indicator.bottom));
+
+ color_white = RrColorNew(ob_rr_inst, 0xff, 0xff, 0xff);
+
+ a_focus_indicator = RrAppearanceNew(ob_rr_inst, 4);
+ a_focus_indicator->surface.grad = RR_SURFACE_SOLID;
+ a_focus_indicator->surface.relief = RR_RELIEF_FLAT;
+ a_focus_indicator->surface.primary = RrColorNew(ob_rr_inst,
+ 0, 0, 0);
+ a_focus_indicator->texture[0].type = RR_TEXTURE_LINE_ART;
+ a_focus_indicator->texture[0].data.lineart.color = color_white;
+ a_focus_indicator->texture[1].type = RR_TEXTURE_LINE_ART;
+ a_focus_indicator->texture[1].data.lineart.color = color_white;
+ a_focus_indicator->texture[2].type = RR_TEXTURE_LINE_ART;
+ a_focus_indicator->texture[2].data.lineart.color = color_white;
+ a_focus_indicator->texture[3].type = RR_TEXTURE_LINE_ART;
+ a_focus_indicator->texture[3].data.lineart.color = color_white;
+}
+
+void focus_cycle_indicator_shutdown(gboolean reconfig)
+{
+ if (reconfig) return;
+
+ RrColorFree(color_white);
+
+ RrAppearanceFree(a_focus_indicator);
+
+ window_remove(focus_indicator.top.window);
+ window_remove(focus_indicator.left.window);
+ window_remove(focus_indicator.right.window);
+ window_remove(focus_indicator.bottom.window);
+
+ stacking_remove(INTERNAL_AS_WINDOW(&focus_indicator.top));
+ stacking_remove(INTERNAL_AS_WINDOW(&focus_indicator.left));
+ stacking_remove(INTERNAL_AS_WINDOW(&focus_indicator.right));
+ stacking_remove(INTERNAL_AS_WINDOW(&focus_indicator.bottom));
+
+ XDestroyWindow(obt_display, focus_indicator.top.window);
+ XDestroyWindow(obt_display, focus_indicator.left.window);
+ XDestroyWindow(obt_display, focus_indicator.right.window);
+ XDestroyWindow(obt_display, focus_indicator.bottom.window);
+}
+
+void focus_cycle_update_indicator(ObClient *c)
+{
+ if (visible)
+ focus_cycle_draw_indicator(c);
+}
+
+void focus_cycle_draw_indicator(ObClient *c)
+{
+ if (!c && visible) {
+ gulong ignore_start;
+
+ /* kill enter events cause by this unmapping */
+ ignore_start = event_start_ignore_all_enters();
+
+ XUnmapWindow(obt_display, focus_indicator.top.window);
+ XUnmapWindow(obt_display, focus_indicator.left.window);
+ XUnmapWindow(obt_display, focus_indicator.right.window);
+ XUnmapWindow(obt_display, focus_indicator.bottom.window);
+
+ event_end_ignore_all_enters(ignore_start);
+
+ visible = FALSE;
+ }
+ else if (c) {
+ /*
+ if (c)
+ frame_adjust_focus(c->frame, FALSE);
+ frame_adjust_focus(c->frame, TRUE);
+ */
+ gint x, y, w, h;
+ gint wt, wl, wr, wb;
+ gulong ignore_start;
+
+ wt = wl = wr = wb = FOCUS_INDICATOR_WIDTH;
+
+ x = c->frame->area.x;
+ y = c->frame->area.y;
+ w = c->frame->area.width;
+ h = wt;
+
+ /* kill enter events cause by this moving */
+ ignore_start = event_start_ignore_all_enters();
+
+ XMoveResizeWindow(obt_display, focus_indicator.top.window,
+ x, y, w, h);
+ a_focus_indicator->texture[0].data.lineart.x1 = 0;
+ a_focus_indicator->texture[0].data.lineart.y1 = h-1;
+ a_focus_indicator->texture[0].data.lineart.x2 = 0;
+ a_focus_indicator->texture[0].data.lineart.y2 = 0;
+ a_focus_indicator->texture[1].data.lineart.x1 = 0;
+ a_focus_indicator->texture[1].data.lineart.y1 = 0;
+ a_focus_indicator->texture[1].data.lineart.x2 = w-1;
+ a_focus_indicator->texture[1].data.lineart.y2 = 0;
+ a_focus_indicator->texture[2].data.lineart.x1 = w-1;
+ a_focus_indicator->texture[2].data.lineart.y1 = 0;
+ a_focus_indicator->texture[2].data.lineart.x2 = w-1;
+ a_focus_indicator->texture[2].data.lineart.y2 = h-1;
+ a_focus_indicator->texture[3].data.lineart.x1 = (wl-1);
+ a_focus_indicator->texture[3].data.lineart.y1 = h-1;
+ a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
+ a_focus_indicator->texture[3].data.lineart.y2 = h-1;
+ RrPaint(a_focus_indicator, focus_indicator.top.window,
+ w, h);
+
+ x = c->frame->area.x;
+ y = c->frame->area.y;
+ w = wl;
+ h = c->frame->area.height;
+
+ XMoveResizeWindow(obt_display, focus_indicator.left.window,
+ x, y, w, h);
+ a_focus_indicator->texture[0].data.lineart.x1 = w-1;
+ a_focus_indicator->texture[0].data.lineart.y1 = 0;
+ a_focus_indicator->texture[0].data.lineart.x2 = 0;
+ a_focus_indicator->texture[0].data.lineart.y2 = 0;
+ a_focus_indicator->texture[1].data.lineart.x1 = 0;
+ a_focus_indicator->texture[1].data.lineart.y1 = 0;
+ a_focus_indicator->texture[1].data.lineart.x2 = 0;
+ a_focus_indicator->texture[1].data.lineart.y2 = h-1;
+ a_focus_indicator->texture[2].data.lineart.x1 = 0;
+ a_focus_indicator->texture[2].data.lineart.y1 = h-1;
+ a_focus_indicator->texture[2].data.lineart.x2 = w-1;
+ a_focus_indicator->texture[2].data.lineart.y2 = h-1;
+ a_focus_indicator->texture[3].data.lineart.x1 = w-1;
+ a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
+ a_focus_indicator->texture[3].data.lineart.x2 = w-1;
+ a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
+ RrPaint(a_focus_indicator, focus_indicator.left.window,
+ w, h);
+
+ x = c->frame->area.x + c->frame->area.width - wr;
+ y = c->frame->area.y;
+ w = wr;
+ h = c->frame->area.height ;
+
+ XMoveResizeWindow(obt_display, focus_indicator.right.window,
+ x, y, w, h);
+ a_focus_indicator->texture[0].data.lineart.x1 = 0;
+ a_focus_indicator->texture[0].data.lineart.y1 = 0;
+ a_focus_indicator->texture[0].data.lineart.x2 = w-1;
+ a_focus_indicator->texture[0].data.lineart.y2 = 0;
+ a_focus_indicator->texture[1].data.lineart.x1 = w-1;
+ a_focus_indicator->texture[1].data.lineart.y1 = 0;
+ a_focus_indicator->texture[1].data.lineart.x2 = w-1;
+ a_focus_indicator->texture[1].data.lineart.y2 = h-1;
+ a_focus_indicator->texture[2].data.lineart.x1 = w-1;
+ a_focus_indicator->texture[2].data.lineart.y1 = h-1;
+ a_focus_indicator->texture[2].data.lineart.x2 = 0;
+ a_focus_indicator->texture[2].data.lineart.y2 = h-1;
+ a_focus_indicator->texture[3].data.lineart.x1 = 0;
+ a_focus_indicator->texture[3].data.lineart.y1 = wt-1;
+ a_focus_indicator->texture[3].data.lineart.x2 = 0;
+ a_focus_indicator->texture[3].data.lineart.y2 = h - wb;
+ RrPaint(a_focus_indicator, focus_indicator.right.window,
+ w, h);
+
+ x = c->frame->area.x;
+ y = c->frame->area.y + c->frame->area.height - wb;
+ w = c->frame->area.width;
+ h = wb;
+
+ XMoveResizeWindow(obt_display, focus_indicator.bottom.window,
+ x, y, w, h);
+ a_focus_indicator->texture[0].data.lineart.x1 = 0;
+ a_focus_indicator->texture[0].data.lineart.y1 = 0;
+ a_focus_indicator->texture[0].data.lineart.x2 = 0;
+ a_focus_indicator->texture[0].data.lineart.y2 = h-1;
+ a_focus_indicator->texture[1].data.lineart.x1 = 0;
+ a_focus_indicator->texture[1].data.lineart.y1 = h-1;
+ a_focus_indicator->texture[1].data.lineart.x2 = w-1;
+ a_focus_indicator->texture[1].data.lineart.y2 = h-1;
+ a_focus_indicator->texture[2].data.lineart.x1 = w-1;
+ a_focus_indicator->texture[2].data.lineart.y1 = h-1;
+ a_focus_indicator->texture[2].data.lineart.x2 = w-1;
+ a_focus_indicator->texture[2].data.lineart.y2 = 0;
+ a_focus_indicator->texture[3].data.lineart.x1 = wl-1;
+ a_focus_indicator->texture[3].data.lineart.y1 = 0;
+ a_focus_indicator->texture[3].data.lineart.x2 = w - wr;
+ a_focus_indicator->texture[3].data.lineart.y2 = 0;
+ RrPaint(a_focus_indicator, focus_indicator.bottom.window,
+ w, h);
+
+ XMapWindow(obt_display, focus_indicator.top.window);
+ XMapWindow(obt_display, focus_indicator.left.window);
+ XMapWindow(obt_display, focus_indicator.right.window);
+ XMapWindow(obt_display, focus_indicator.bottom.window);
+
+ event_end_ignore_all_enters(ignore_start);
+
+ visible = TRUE;
+ }
+}
diff --git a/openbox/focus_cycle_indicator.h b/openbox/focus_cycle_indicator.h
new file mode 100644
index 0000000..3077f7c
--- /dev/null
+++ b/openbox/focus_cycle_indicator.h
@@ -0,0 +1,31 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ focus_cycle_indicator.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __focus_cycle_indicator_h
+#define __focus_cycle_indicator_h
+
+struct _ObClient;
+
+void focus_cycle_indicator_startup(gboolean reconfig);
+void focus_cycle_indicator_shutdown(gboolean reconfig);
+
+void focus_cycle_update_indicator(struct _ObClient *c);
+void focus_cycle_draw_indicator(struct _ObClient *c);
+
+#endif
diff --git a/openbox/focus_cycle_popup.c b/openbox/focus_cycle_popup.c
new file mode 100644
index 0000000..e1ea848
--- /dev/null
+++ b/openbox/focus_cycle_popup.c
@@ -0,0 +1,847 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ focus_cycle_popup.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "focus_cycle_popup.h"
+#include "focus_cycle.h"
+#include "popup.h"
+#include "client.h"
+#include "screen.h"
+#include "focus.h"
+#include "openbox.h"
+#include "config.h"
+#include "window.h"
+#include "event.h"
+#include "obrender/render.h"
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+/* Size of the icons, which can appear inside or outside of a hilite box */
+#define ICON_SIZE (gint)config_theme_window_list_icon_size
+/* Size of the hilite box around a window's icon */
+#define HILITE_SIZE (ICON_SIZE + 2*HILITE_OFFSET)
+/* Width of the outer ring around the hilite box */
+#define HILITE_WIDTH 2
+/* Space between the outer ring around the hilite box and the icon inside it */
+#define HILITE_MARGIN 1
+/* Total distance from the edge of the hilite box to the icon inside it */
+#define HILITE_OFFSET (HILITE_WIDTH + HILITE_MARGIN)
+/* Margin area around the outside of the dialog */
+#define OUTSIDE_BORDER 3
+/* Margin area around the text */
+#define TEXT_BORDER 2
+/* Scroll the list-mode list when the cursor gets within this many rows of the
+ top or bottom */
+#define SCROLL_MARGIN 4
+
+typedef struct _ObFocusCyclePopup ObFocusCyclePopup;
+typedef struct _ObFocusCyclePopupTarget ObFocusCyclePopupTarget;
+
+struct _ObFocusCyclePopupTarget
+{
+ ObClient *client;
+ RrImage *icon;
+ gchar *text;
+ Window iconwin;
+ /* This is used when the popup is in list mode */
+ Window textwin;
+};
+
+struct _ObFocusCyclePopup
+{
+ ObWindow obwin;
+ Window bg;
+
+ /* This is used when the popup is in icon mode */
+ Window icon_mode_text;
+
+ Window list_mode_up;
+ Window list_mode_down;
+
+ GList *targets;
+ gint n_targets;
+
+ const ObFocusCyclePopupTarget *last_target;
+
+ gint maxtextw;
+
+ /* How are the list is scrolled, in scroll mode */
+ gint scroll;
+
+ RrAppearance *a_bg;
+ RrAppearance *a_text;
+ RrAppearance *a_hilite_text;
+ RrAppearance *a_icon;
+ RrAppearance *a_arrow;
+
+ gboolean mapped;
+ ObFocusCyclePopupMode mode;
+};
+
+/*! This popup shows all possible windows */
+static ObFocusCyclePopup popup;
+/*! This popup shows a single window */
+static ObIconPopup *single_popup;
+
+static gchar *popup_get_name (ObClient *c);
+static gboolean popup_setup (ObFocusCyclePopup *p,
+ gboolean create_targets,
+ gboolean refresh_targets,
+ gboolean linear);
+static void popup_render (ObFocusCyclePopup *p,
+ const ObClient *c);
+
+static Window create_window(Window parent, guint bwidth, gulong mask,
+ XSetWindowAttributes *attr)
+{
+ return XCreateWindow(obt_display, parent, 0, 0, 1, 1, bwidth,
+ RrDepth(ob_rr_inst), InputOutput,
+ RrVisual(ob_rr_inst), mask, attr);
+}
+
+void focus_cycle_popup_startup(gboolean reconfig)
+{
+ XSetWindowAttributes attrib;
+ RrPixel32 *p;
+
+ single_popup = icon_popup_new();
+
+ popup.obwin.type = OB_WINDOW_CLASS_INTERNAL;
+ popup.a_bg = RrAppearanceCopy(ob_rr_theme->osd_bg);
+ popup.a_hilite_text = RrAppearanceCopy(ob_rr_theme->osd_hilite_label);
+ popup.a_text = RrAppearanceCopy(ob_rr_theme->osd_unhilite_label);
+ popup.a_icon = RrAppearanceCopy(ob_rr_theme->a_clear);
+ popup.a_arrow = RrAppearanceCopy(ob_rr_theme->a_clear_tex);
+
+ popup.a_hilite_text->surface.parent = popup.a_bg;
+ popup.a_text->surface.parent = popup.a_bg;
+ popup.a_icon->surface.parent = popup.a_bg;
+
+ popup.a_text->texture[0].data.text.justify = RR_JUSTIFY_LEFT;
+ popup.a_hilite_text->texture[0].data.text.justify = RR_JUSTIFY_LEFT;
+
+ /* 2 textures. texture[0] is the icon. texture[1] is the hilight, and
+ may or may not be used */
+ RrAppearanceAddTextures(popup.a_icon, 2);
+
+ RrAppearanceClearTextures(popup.a_icon);
+ popup.a_icon->texture[0].type = RR_TEXTURE_IMAGE;
+
+ RrAppearanceClearTextures(popup.a_arrow);
+ popup.a_arrow->texture[0].type = RR_TEXTURE_MASK;
+ popup.a_arrow->texture[0].data.mask.color =
+ ob_rr_theme->osd_text_active_color;
+
+ attrib.override_redirect = True;
+ attrib.border_pixel=RrColorPixel(ob_rr_theme->osd_border_color);
+ popup.bg = create_window(obt_root(ob_screen), ob_rr_theme->obwidth,
+ CWOverrideRedirect | CWBorderPixel, &attrib);
+
+ /* create the text window used for the icon-mode popup */
+ popup.icon_mode_text = create_window(popup.bg, 0, 0, NULL);
+
+ /* create the windows for the up and down arrows */
+ popup.list_mode_up = create_window(popup.bg, 0, 0, NULL);
+ popup.list_mode_down = create_window(popup.bg, 0, 0, NULL);
+
+ popup.targets = NULL;
+ popup.n_targets = 0;
+ popup.last_target = NULL;
+
+ /* set up the hilite texture for the icon */
+ popup.a_icon->texture[1].data.rgba.width = HILITE_SIZE;
+ popup.a_icon->texture[1].data.rgba.height = HILITE_SIZE;
+ popup.a_icon->texture[1].data.rgba.alpha = 0xff;
+ p = g_new(RrPixel32, HILITE_SIZE * HILITE_SIZE);
+ popup.a_icon->texture[1].data.rgba.data = p;
+
+ /* create the hilite under the target icon */
+ {
+ RrPixel32 color;
+ RrColor *tc;
+ gint x, y, o;
+
+ tc = ob_rr_theme->osd_text_active_color;
+ color = ((tc->r & 0xff) << RrDefaultRedOffset) +
+ ((tc->g & 0xff) << RrDefaultGreenOffset) +
+ ((tc->b & 0xff) << RrDefaultBlueOffset);
+
+ o = 0;
+ for (x = 0; x < HILITE_SIZE; x++)
+ for (y = 0; y < HILITE_SIZE; y++) {
+ guchar a;
+
+ if (x < HILITE_WIDTH ||
+ x >= HILITE_SIZE - HILITE_WIDTH ||
+ y < HILITE_WIDTH ||
+ y >= HILITE_SIZE - HILITE_WIDTH)
+ {
+ /* the border of the target */
+ a = 0x88;
+ } else {
+ /* the background of the target */
+ a = 0x22;
+ }
+
+ p[o++] = color + (a << RrDefaultAlphaOffset);
+ }
+ }
+
+ stacking_add(INTERNAL_AS_WINDOW(&popup));
+ window_add(&popup.bg, INTERNAL_AS_WINDOW(&popup));
+}
+
+void focus_cycle_popup_shutdown(gboolean reconfig)
+{
+ icon_popup_free(single_popup);
+
+ window_remove(popup.bg);
+ stacking_remove(INTERNAL_AS_WINDOW(&popup));
+
+ while(popup.targets) {
+ ObFocusCyclePopupTarget *t = popup.targets->data;
+
+ RrImageUnref(t->icon);
+ g_free(t->text);
+ XDestroyWindow(obt_display, t->iconwin);
+ XDestroyWindow(obt_display, t->textwin);
+ g_slice_free(ObFocusCyclePopupTarget, t);
+
+ popup.targets = g_list_delete_link(popup.targets, popup.targets);
+ }
+
+ g_free(popup.a_icon->texture[1].data.rgba.data);
+ popup.a_icon->texture[1].data.rgba.data = NULL;
+
+ XDestroyWindow(obt_display, popup.list_mode_up);
+ XDestroyWindow(obt_display, popup.list_mode_down);
+ XDestroyWindow(obt_display, popup.icon_mode_text);
+ XDestroyWindow(obt_display, popup.bg);
+
+ RrAppearanceFree(popup.a_arrow);
+ RrAppearanceFree(popup.a_icon);
+ RrAppearanceFree(popup.a_hilite_text);
+ RrAppearanceFree(popup.a_text);
+ RrAppearanceFree(popup.a_bg);
+}
+
+static void popup_target_free(ObFocusCyclePopupTarget *t)
+{
+ RrImageUnref(t->icon);
+ g_free(t->text);
+ XDestroyWindow(obt_display, t->iconwin);
+ XDestroyWindow(obt_display, t->textwin);
+ g_slice_free(ObFocusCyclePopupTarget, t);
+}
+
+static gboolean popup_setup(ObFocusCyclePopup *p, gboolean create_targets,
+ gboolean refresh_targets, gboolean linear)
+{
+ gint maxwidth, n;
+ GList *it;
+ GList *rtargets; /* old targets for refresh */
+ GList *rtlast;
+ gboolean change;
+
+ if (refresh_targets) {
+ rtargets = p->targets;
+ rtlast = g_list_last(rtargets);
+ p->targets = NULL;
+ p->n_targets = 0;
+ change = FALSE;
+ }
+ else {
+ rtargets = rtlast = NULL;
+ change = TRUE;
+ }
+
+ g_assert(p->targets == NULL);
+ g_assert(p->n_targets == 0);
+
+ /* make its width to be the width of all the possible titles */
+
+ /* build a list of all the valid focus targets and measure their strings,
+ and count them */
+ maxwidth = 0;
+ n = 0;
+ for (it = g_list_last(linear ? client_list : focus_order);
+ it;
+ it = g_list_previous(it))
+ {
+ ObClient *ft = it->data;
+
+ if (focus_cycle_valid(ft)) {
+ GList *rit;
+
+ /* reuse the target if possible during refresh */
+ for (rit = rtlast; rit; rit = g_list_previous(rit)) {
+ ObFocusCyclePopupTarget *t = rit->data;
+ if (t->client == ft) {
+ if (rit == rtlast)
+ rtlast = g_list_previous(rit);
+ rtargets = g_list_remove_link(rtargets, rit);
+
+ p->targets = g_list_concat(rit, p->targets);
+ ++n;
+
+ if (rit != rtlast)
+ change = TRUE; /* order changed */
+ break;
+ }
+ }
+
+ if (!rit) {
+ gchar *text = popup_get_name(ft);
+
+ /* measure */
+ p->a_text->texture[0].data.text.string = text;
+ maxwidth = MAX(maxwidth, RrMinWidth(p->a_text));
+
+ if (!create_targets) {
+ g_free(text);
+ } else {
+ ObFocusCyclePopupTarget *t =
+ g_slice_new(ObFocusCyclePopupTarget);
+
+ t->client = ft;
+ t->text = text;
+ t->icon = client_icon(t->client);
+ RrImageRef(t->icon); /* own the icon so it won't go away */
+ t->iconwin = create_window(p->bg, 0, 0, NULL);
+ t->textwin = create_window(p->bg, 0, 0, NULL);
+
+ p->targets = g_list_prepend(p->targets, t);
+ ++n;
+
+ change = TRUE; /* added a window */
+ }
+ }
+ }
+ }
+
+ if (rtargets) {
+ change = TRUE; /* removed a window */
+
+ while (rtargets) {
+ popup_target_free(rtargets->data);
+ rtargets = g_list_delete_link(rtargets, rtargets);
+ }
+ }
+
+ p->n_targets = n;
+ if (refresh_targets)
+ /* don't shrink when refreshing */
+ p->maxtextw = MAX(p->maxtextw, maxwidth);
+ else
+ p->maxtextw = maxwidth;
+
+ return change;
+}
+
+static void popup_cleanup(void)
+{
+ while(popup.targets) {
+ popup_target_free(popup.targets->data);
+ popup.targets = g_list_delete_link(popup.targets, popup.targets);
+ }
+ popup.n_targets = 0;
+ popup.last_target = NULL;
+}
+
+static gchar *popup_get_name(ObClient *c)
+{
+ ObClient *p;
+ gchar *title;
+ const gchar *desk = NULL;
+ gchar *ret;
+
+ /* find our highest direct parent */
+ p = client_search_top_direct_parent(c);
+
+ if (c->desktop != DESKTOP_ALL && c->desktop != screen_desktop)
+ desk = screen_desktop_names[c->desktop];
+
+ title = c->iconic ? c->icon_title : c->title;
+
+ /* use the transient's parent's title/icon if we don't have one */
+ if (p != c && title[0] == '\0')
+ title = p->iconic ? p->icon_title : p->title;
+
+ if (desk)
+ ret = g_strdup_printf("%s [%s]", title, desk);
+ else
+ ret = g_strdup(title);
+
+ return ret;
+}
+
+static void popup_render(ObFocusCyclePopup *p, const ObClient *c)
+{
+ gint ml, mt, mr, mb;
+ gint l, t, r, b;
+ gint x, y, w, h;
+ const Rect *screen_area = NULL;
+ gint i;
+ GList *it;
+ const ObFocusCyclePopupTarget *newtarget;
+ ObFocusCyclePopupMode mode = p->mode;
+ gint icons_per_row;
+ gint icon_rows;
+ gint textw, texth;
+ gint selected_pos;
+ gint last_scroll;
+
+ /* vars for icon mode */
+ gint icon_mode_textx;
+ gint icon_mode_texty;
+ gint icons_center_x;
+
+ /* vars for list mode */
+ gint list_mode_icon_column_w = HILITE_SIZE + OUTSIDE_BORDER;
+ gint up_arrow_x, down_arrow_x;
+ gint up_arrow_y, down_arrow_y;
+ gboolean showing_arrows = FALSE;
+
+ g_assert(mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS ||
+ mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST);
+
+ screen_area = screen_physical_area_primary(FALSE);
+
+ /* get the outside margins */
+ RrMargins(p->a_bg, &ml, &mt, &mr, &mb);
+
+ /* get our outside borders */
+ l = ml + OUTSIDE_BORDER;
+ r = mr + OUTSIDE_BORDER;
+ t = mt + OUTSIDE_BORDER;
+ b = mb + OUTSIDE_BORDER;
+
+ /* get the width from the text and keep it within limits */
+ w = l + r + p->maxtextw;
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
+ /* when in list mode, there are icons down the side */
+ w += list_mode_icon_column_w;
+ w = MIN(w, MAX(screen_area->width/3, POPUP_WIDTH)); /* max width */
+ w = MAX(w, POPUP_WIDTH); /* min width */
+
+ /* get the text height */
+ texth = RrMinHeight(p->a_hilite_text);
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
+ texth = MAX(MAX(texth, RrMinHeight(p->a_text)), HILITE_SIZE);
+ else
+ texth += TEXT_BORDER * 2;
+
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS) {
+ /* how many icons will fit in that row? make the width fit that */
+ w -= l + r;
+ icons_per_row = (w + HILITE_SIZE - 1) / HILITE_SIZE;
+ w = icons_per_row * HILITE_SIZE + l + r;
+
+ /* how many rows do we need? */
+ icon_rows = (p->n_targets-1) / icons_per_row + 1;
+ } else {
+ /* in list mode, there is one column of icons.. */
+ icons_per_row = 1;
+ /* maximum is 80% of the screen height */
+ icon_rows = MIN(p->n_targets,
+ (4*screen_area->height/5) /* 80% of the screen */
+ /
+ MAX(HILITE_SIZE, texth)); /* height of each row */
+ /* but make sure there is always one */
+ icon_rows = MAX(icon_rows, 1);
+ }
+
+ /* get the text width */
+ textw = w - l - r;
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
+ /* leave space on the side for the icons */
+ textw -= list_mode_icon_column_w;
+
+ if (!p->mapped)
+ /* reset the scrolling when the dialog is first shown */
+ p->scroll = 0;
+
+ /* find the height of the dialog */
+ h = t + b + (icon_rows * MAX(HILITE_SIZE, texth));
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS)
+ /* in icon mode the text sits below the icons, so make some space */
+ h += OUTSIDE_BORDER + texth;
+
+ /* find the focused target */
+ newtarget = NULL;
+ for (i = 0, it = p->targets; it; ++i, it = g_list_next(it)) {
+ const ObFocusCyclePopupTarget *target = it->data;
+ if (target->client == c) {
+ /* save the target */
+ newtarget = target;
+ break;
+ }
+ }
+ selected_pos = i;
+ g_assert(newtarget != NULL);
+
+ /* scroll the list if needed */
+ last_scroll = p->scroll;
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) {
+ const gint top = p->scroll + SCROLL_MARGIN;
+ const gint bottom = p->scroll + icon_rows - SCROLL_MARGIN;
+ const gint min_scroll = 0;
+ const gint max_scroll = p->n_targets - icon_rows;
+
+ if (top - selected_pos >= 0) {
+ p->scroll -= top - selected_pos + 1;
+ p->scroll = MAX(p->scroll, min_scroll);
+ } else if (selected_pos - bottom >= 0) {
+ p->scroll += selected_pos - bottom + 1;
+ p->scroll = MIN(p->scroll, max_scroll);
+ }
+ }
+
+ /* show the scroll arrows when appropriate */
+ if (p->scroll && mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) {
+ XMapWindow(obt_display, p->list_mode_up);
+ showing_arrows = TRUE;
+ } else
+ XUnmapWindow(obt_display, p->list_mode_up);
+
+ if (p->scroll < p->n_targets - icon_rows &&
+ mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
+ {
+ XMapWindow(obt_display, p->list_mode_down);
+ showing_arrows = TRUE;
+ } else
+ XUnmapWindow(obt_display, p->list_mode_down);
+
+ /* make space for the arrows */
+ if (showing_arrows)
+ h += ob_rr_theme->up_arrow_mask->height + OUTSIDE_BORDER
+ + ob_rr_theme->down_arrow_mask->height + OUTSIDE_BORDER;
+
+ /* center the icons if there is less than one row */
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS && icon_rows == 1)
+ icons_center_x = (w - p->n_targets * HILITE_SIZE) / 2;
+ else
+ icons_center_x = 0;
+
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS) {
+ /* get the position of the text */
+ icon_mode_textx = l;
+ icon_mode_texty = h - texth - b;
+ }
+
+ /* find the position for the popup (include the outer borders) */
+ x = screen_area->x + (screen_area->width -
+ (w + ob_rr_theme->obwidth * 2)) / 2;
+ y = screen_area->y + (screen_area->height -
+ (h + ob_rr_theme->obwidth * 2)) / 2;
+
+ if (!p->mapped) {
+ /* position the background but don't draw it */
+ XMoveResizeWindow(obt_display, p->bg, x, y, w, h);
+
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS) {
+ /* position the text */
+ XMoveResizeWindow(obt_display, p->icon_mode_text,
+ icon_mode_textx, icon_mode_texty, textw, texth);
+ XMapWindow(obt_display, popup.icon_mode_text);
+ } else {
+ XUnmapWindow(obt_display, popup.icon_mode_text);
+
+ up_arrow_x = (w - ob_rr_theme->up_arrow_mask->width) / 2;
+ up_arrow_y = t;
+
+ down_arrow_x = (w - ob_rr_theme->down_arrow_mask->width) / 2;
+ down_arrow_y = h - b - ob_rr_theme->down_arrow_mask->height;
+
+ /* position the arrows */
+ XMoveResizeWindow(obt_display, p->list_mode_up,
+ up_arrow_x, up_arrow_y,
+ ob_rr_theme->up_arrow_mask->width,
+ ob_rr_theme->up_arrow_mask->height);
+ XMoveResizeWindow(obt_display, p->list_mode_down,
+ down_arrow_x, down_arrow_y,
+ ob_rr_theme->down_arrow_mask->width,
+ ob_rr_theme->down_arrow_mask->height);
+ }
+ }
+
+ /* * * draw everything * * */
+
+ /* draw the background */
+ if (!p->mapped)
+ RrPaint(p->a_bg, p->bg, w, h);
+
+ /* draw the scroll arrows */
+ if (!p->mapped && mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) {
+ p->a_arrow->texture[0].data.mask.mask =
+ ob_rr_theme->up_arrow_mask;
+ p->a_arrow->surface.parent = p->a_bg;
+ p->a_arrow->surface.parentx = up_arrow_x;
+ p->a_arrow->surface.parenty = up_arrow_y;
+ RrPaint(p->a_arrow, p->list_mode_up,
+ ob_rr_theme->up_arrow_mask->width,
+ ob_rr_theme->up_arrow_mask->height);
+
+ p->a_arrow->texture[0].data.mask.mask =
+ ob_rr_theme->down_arrow_mask;
+ p->a_arrow->surface.parent = p->a_bg;
+ p->a_arrow->surface.parentx = down_arrow_x;
+ p->a_arrow->surface.parenty = down_arrow_y;
+ RrPaint(p->a_arrow, p->list_mode_down,
+ ob_rr_theme->down_arrow_mask->width,
+ ob_rr_theme->down_arrow_mask->height);
+ }
+
+ /* draw the icons and text */
+ for (i = 0, it = p->targets; it; ++i, it = g_list_next(it)) {
+ const ObFocusCyclePopupTarget *target = it->data;
+
+ /* have to redraw the targetted icon and last targetted icon
+ * to update the hilite */
+ if (!p->mapped || newtarget == target || p->last_target == target ||
+ last_scroll != p->scroll)
+ {
+ /* row and column start from 0 */
+ const gint row = i / icons_per_row - p->scroll;
+ const gint col = i % icons_per_row;
+ gint iconx, icony;
+ gint list_mode_textx, list_mode_texty;
+ RrAppearance *text;
+
+ /* find the coordinates for the icon */
+ iconx = icons_center_x + l + (col * HILITE_SIZE);
+ icony = t + (showing_arrows ? ob_rr_theme->up_arrow_mask->height
+ + OUTSIDE_BORDER
+ : 0)
+ + (row * MAX(texth, HILITE_SIZE))
+ + MAX(texth - HILITE_SIZE, 0) / 2;
+
+ /* find the dimensions of the text box */
+ list_mode_textx = iconx + HILITE_SIZE + TEXT_BORDER;
+ list_mode_texty = icony;
+
+ /* position the icon */
+ XMoveResizeWindow(obt_display, target->iconwin,
+ iconx, icony, HILITE_SIZE, HILITE_SIZE);
+
+ /* position the text */
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
+ XMoveResizeWindow(obt_display, target->textwin,
+ list_mode_textx, list_mode_texty,
+ textw, texth);
+
+ /* show/hide the right windows */
+ if (row >= 0 && row < icon_rows) {
+ XMapWindow(obt_display, target->iconwin);
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
+ XMapWindow(obt_display, target->textwin);
+ else
+ XUnmapWindow(obt_display, target->textwin);
+ } else {
+ XUnmapWindow(obt_display, target->textwin);
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST)
+ XUnmapWindow(obt_display, target->iconwin);
+ else
+ XMapWindow(obt_display, target->iconwin);
+ }
+
+ /* get the icon from the client */
+ p->a_icon->texture[0].data.image.twidth = ICON_SIZE;
+ p->a_icon->texture[0].data.image.theight = ICON_SIZE;
+ p->a_icon->texture[0].data.image.tx = HILITE_OFFSET;
+ p->a_icon->texture[0].data.image.ty = HILITE_OFFSET;
+ p->a_icon->texture[0].data.image.alpha =
+ target->client->iconic ? OB_ICONIC_ALPHA : 0xff;
+ p->a_icon->texture[0].data.image.image = target->icon;
+
+ /* Draw the hilite? */
+ p->a_icon->texture[1].type = (target == newtarget) ?
+ RR_TEXTURE_RGBA : RR_TEXTURE_NONE;
+
+ /* draw the icon */
+ p->a_icon->surface.parentx = iconx;
+ p->a_icon->surface.parenty = icony;
+ RrPaint(p->a_icon, target->iconwin, HILITE_SIZE, HILITE_SIZE);
+
+ /* draw the text */
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST ||
+ target == newtarget)
+ {
+ text = (target == newtarget) ? p->a_hilite_text : p->a_text;
+ text->texture[0].data.text.string = target->text;
+ text->surface.parentx =
+ mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS ?
+ icon_mode_textx : list_mode_textx;
+ text->surface.parenty =
+ mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS ?
+ icon_mode_texty : list_mode_texty;
+ RrPaint(text,
+ (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS ?
+ p->icon_mode_text : target->textwin),
+ textw, texth);
+ }
+ }
+ }
+
+ p->last_target = newtarget;
+
+ XFlush(obt_display);
+}
+
+void focus_cycle_popup_show(ObClient *c, ObFocusCyclePopupMode mode,
+ gboolean linear)
+{
+ g_assert(c != NULL);
+
+ if (mode == OB_FOCUS_CYCLE_POPUP_MODE_NONE) {
+ focus_cycle_popup_hide();
+ return;
+ }
+
+ /* do this stuff only when the dialog is first showing */
+ if (!popup.mapped) {
+ popup_setup(&popup, TRUE, FALSE, linear);
+ /* this is fixed once the dialog is shown */
+ popup.mode = mode;
+ }
+ g_assert(popup.targets != NULL);
+
+ popup_render(&popup, c);
+
+ if (!popup.mapped) {
+ /* show the dialog */
+ XMapWindow(obt_display, popup.bg);
+ XFlush(obt_display);
+ popup.mapped = TRUE;
+ screen_hide_desktop_popup();
+ }
+}
+
+void focus_cycle_popup_hide(void)
+{
+ gulong ignore_start;
+
+ ignore_start = event_start_ignore_all_enters();
+
+ XUnmapWindow(obt_display, popup.bg);
+ XFlush(obt_display);
+
+ event_end_ignore_all_enters(ignore_start);
+
+ popup.mapped = FALSE;
+
+ popup_cleanup();
+}
+
+void focus_cycle_popup_single_show(struct _ObClient *c)
+{
+ gchar *text;
+
+ g_assert(c != NULL);
+
+ /* do this stuff only when the dialog is first showing */
+ if (!single_popup->popup->mapped) {
+ const Rect *a;
+
+ /* position the popup */
+ a = screen_physical_area_primary(FALSE);
+ icon_popup_position(single_popup, CenterGravity,
+ a->x + a->width / 2, a->y + a->height / 2);
+ icon_popup_height(single_popup, POPUP_HEIGHT);
+ icon_popup_min_width(single_popup, POPUP_WIDTH);
+ icon_popup_max_width(single_popup, MAX(a->width/3, POPUP_WIDTH));
+ icon_popup_text_width(single_popup, popup.maxtextw);
+ }
+
+ text = popup_get_name(c);
+ icon_popup_show(single_popup, text, client_icon(c));
+ g_free(text);
+ screen_hide_desktop_popup();
+}
+
+void focus_cycle_popup_single_hide(void)
+{
+ icon_popup_hide(single_popup);
+}
+
+gboolean focus_cycle_popup_is_showing(ObClient *c)
+{
+ if (popup.mapped) {
+ GList *it;
+
+ for (it = popup.targets; it; it = g_list_next(it)) {
+ ObFocusCyclePopupTarget *t = it->data;
+ if (t->client == c)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static ObClient* popup_revert(ObClient *target)
+{
+ GList *it, *itt;
+
+ for (it = popup.targets; it; it = g_list_next(it)) {
+ ObFocusCyclePopupTarget *t = it->data;
+ if (t->client == target) {
+ /* move to a previous window if possible */
+ for (itt = it->prev; itt; itt = g_list_previous(itt)) {
+ ObFocusCyclePopupTarget *t2 = itt->data;
+ if (focus_cycle_valid(t2->client))
+ return t2->client;
+ }
+
+ /* otherwise move to a following window if possible */
+ for (itt = it->next; itt; itt = g_list_next(itt)) {
+ ObFocusCyclePopupTarget *t2 = itt->data;
+ if (focus_cycle_valid(t2->client))
+ return t2->client;
+ }
+
+ /* otherwise, we can't go anywhere there is nowhere valid to go */
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+ObClient* focus_cycle_popup_refresh(ObClient *target,
+ gboolean redraw,
+ gboolean linear)
+{
+ if (!popup.mapped) return NULL;
+
+ if (!focus_cycle_valid(target))
+ target = popup_revert(target);
+
+ redraw = popup_setup(&popup, TRUE, TRUE, linear) && redraw;
+
+ if (!target && popup.targets)
+ target = ((ObFocusCyclePopupTarget*)popup.targets->data)->client;
+
+ if (target && redraw) {
+ popup.mapped = FALSE;
+ popup_render(&popup, target);
+ XFlush(obt_display);
+ popup.mapped = TRUE;
+ }
+
+ return target;
+}
diff --git a/openbox/focus_cycle_popup.h b/openbox/focus_cycle_popup.h
new file mode 100644
index 0000000..8c80bfc
--- /dev/null
+++ b/openbox/focus_cycle_popup.h
@@ -0,0 +1,53 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ focus_cycle.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __focus_cycle_popup_h
+#define __focus_cycle_popup_h
+
+struct _ObClient;
+
+#include <glib.h>
+
+typedef enum {
+ OB_FOCUS_CYCLE_POPUP_MODE_NONE,
+ OB_FOCUS_CYCLE_POPUP_MODE_ICONS,
+ OB_FOCUS_CYCLE_POPUP_MODE_LIST
+} ObFocusCyclePopupMode;
+
+void focus_cycle_popup_startup(gboolean reconfig);
+void focus_cycle_popup_shutdown(gboolean reconfig);
+
+void focus_cycle_popup_show(struct _ObClient *c, ObFocusCyclePopupMode mode,
+ gboolean linear);
+void focus_cycle_popup_hide(void);
+
+void focus_cycle_popup_single_show(struct _ObClient *c);
+void focus_cycle_popup_single_hide(void);
+
+gboolean focus_cycle_popup_is_showing(struct _ObClient *c);
+
+/*! Redraws the focus cycle popup, and returns the current target. If
+ the target given to the function is no longer valid, this will return
+ a different target that is valid, and which should be considered the
+ current focus cycling target. */
+struct _ObClient *focus_cycle_popup_refresh(struct _ObClient *target,
+ gboolean redraw,
+ gboolean linear);
+
+#endif
diff --git a/openbox/frame.c b/openbox/frame.c
new file mode 100644
index 0000000..48dda24
--- /dev/null
+++ b/openbox/frame.c
@@ -0,0 +1,1866 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ frame.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "frame.h"
+#include "client.h"
+#include "openbox.h"
+#include "grab.h"
+#include "debug.h"
+#include "config.h"
+#include "framerender.h"
+#include "focus_cycle.h"
+#include "focus_cycle_indicator.h"
+#include "moveresize.h"
+#include "screen.h"
+#include "obrender/theme.h"
+#include "obt/display.h"
+#include "obt/xqueue.h"
+#include "obt/prop.h"
+
+#define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
+ ButtonPressMask | ButtonReleaseMask | \
+ SubstructureRedirectMask | FocusChangeMask)
+#define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
+ ButtonMotionMask | PointerMotionMask | \
+ EnterWindowMask | LeaveWindowMask)
+
+#define FRAME_ANIMATE_ICONIFY_TIME 150000 /* .15 seconds */
+#define FRAME_ANIMATE_ICONIFY_STEP_TIME (1000 / 60) /* 60 Hz */
+
+#define FRAME_HANDLE_Y(f) (f->size.top + f->client->area.height + f->cbwidth_b)
+
+static void flash_done(gpointer data);
+static gboolean flash_timeout(gpointer data);
+
+static void layout_title(ObFrame *self);
+static void set_theme_statics(ObFrame *self);
+static void free_theme_statics(ObFrame *self);
+static gboolean frame_animate_iconify(gpointer self);
+static void frame_adjust_cursors(ObFrame *self);
+
+static Window createWindow(Window parent, Visual *visual,
+ gulong mask, XSetWindowAttributes *attrib)
+{
+ return XCreateWindow(obt_display, parent, 0, 0, 1, 1, 0,
+ (visual ? 32 : RrDepth(ob_rr_inst)), InputOutput,
+ (visual ? visual : RrVisual(ob_rr_inst)),
+ mask, attrib);
+
+}
+
+static Visual *check_32bit_client(ObClient *c)
+{
+ XWindowAttributes wattrib;
+ Status ret;
+
+ /* we're already running at 32 bit depth, yay. we don't need to use their
+ visual */
+ if (RrDepth(ob_rr_inst) == 32)
+ return NULL;
+
+ ret = XGetWindowAttributes(obt_display, c->window, &wattrib);
+ g_assert(ret != BadDrawable);
+ g_assert(ret != BadWindow);
+
+ if (wattrib.depth == 32)
+ return wattrib.visual;
+ return NULL;
+}
+
+ObFrame *frame_new(ObClient *client)
+{
+ XSetWindowAttributes attrib;
+ gulong mask;
+ ObFrame *self;
+ Visual *visual;
+
+ self = g_slice_new0(ObFrame);
+ self->client = client;
+
+ visual = check_32bit_client(client);
+
+ /* create the non-visible decor windows */
+
+ mask = 0;
+ if (visual) {
+ /* client has a 32-bit visual */
+ mask = CWColormap | CWBackPixel | CWBorderPixel;
+ /* create a colormap with the visual */
+ self->colormap = attrib.colormap =
+ XCreateColormap(obt_display, obt_root(ob_screen),
+ visual, AllocNone);
+ attrib.background_pixel = BlackPixel(obt_display, ob_screen);
+ attrib.border_pixel = BlackPixel(obt_display, ob_screen);
+ }
+ self->window = createWindow(obt_root(ob_screen), visual,
+ mask, &attrib);
+
+ /* create the visible decor windows */
+
+ mask = 0;
+ if (visual) {
+ /* client has a 32-bit visual */
+ mask = CWColormap | CWBackPixel | CWBorderPixel;
+ attrib.colormap = RrColormap(ob_rr_inst);
+ }
+
+ self->backback = createWindow(self->window, NULL, mask, &attrib);
+ self->backfront = createWindow(self->backback, NULL, mask, &attrib);
+
+ mask |= CWEventMask;
+ attrib.event_mask = ELEMENT_EVENTMASK;
+ self->innerleft = createWindow(self->window, NULL, mask, &attrib);
+ self->innertop = createWindow(self->window, NULL, mask, &attrib);
+ self->innerright = createWindow(self->window, NULL, mask, &attrib);
+ self->innerbottom = createWindow(self->window, NULL, mask, &attrib);
+
+ self->innerblb = createWindow(self->innerbottom, NULL, mask, &attrib);
+ self->innerbrb = createWindow(self->innerbottom, NULL, mask, &attrib);
+ self->innerbll = createWindow(self->innerleft, NULL, mask, &attrib);
+ self->innerbrr = createWindow(self->innerright, NULL, mask, &attrib);
+
+ self->title = createWindow(self->window, NULL, mask, &attrib);
+ self->titleleft = createWindow(self->window, NULL, mask, &attrib);
+ self->titletop = createWindow(self->window, NULL, mask, &attrib);
+ self->titletopleft = createWindow(self->window, NULL, mask, &attrib);
+ self->titletopright = createWindow(self->window, NULL, mask, &attrib);
+ self->titleright = createWindow(self->window, NULL, mask, &attrib);
+ self->titlebottom = createWindow(self->window, NULL, mask, &attrib);
+
+ self->topresize = createWindow(self->title, NULL, mask, &attrib);
+ self->tltresize = createWindow(self->title, NULL, mask, &attrib);
+ self->tllresize = createWindow(self->title, NULL, mask, &attrib);
+ self->trtresize = createWindow(self->title, NULL, mask, &attrib);
+ self->trrresize = createWindow(self->title, NULL, mask, &attrib);
+
+ self->left = createWindow(self->window, NULL, mask, &attrib);
+ self->right = createWindow(self->window, NULL, mask, &attrib);
+
+ self->label = createWindow(self->title, NULL, mask, &attrib);
+ self->max = createWindow(self->title, NULL, mask, &attrib);
+ self->close = createWindow(self->title, NULL, mask, &attrib);
+ self->desk = createWindow(self->title, NULL, mask, &attrib);
+ self->shade = createWindow(self->title, NULL, mask, &attrib);
+ self->icon = createWindow(self->title, NULL, mask, &attrib);
+ self->iconify = createWindow(self->title, NULL, mask, &attrib);
+
+ self->handle = createWindow(self->window, NULL, mask, &attrib);
+ self->lgrip = createWindow(self->handle, NULL, mask, &attrib);
+ self->rgrip = createWindow(self->handle, NULL, mask, &attrib);
+
+ self->handleleft = createWindow(self->handle, NULL, mask, &attrib);
+ self->handleright = createWindow(self->handle, NULL, mask, &attrib);
+
+ self->handletop = createWindow(self->window, NULL, mask, &attrib);
+ self->handlebottom = createWindow(self->window, NULL, mask, &attrib);
+ self->lgripleft = createWindow(self->window, NULL, mask, &attrib);
+ self->lgriptop = createWindow(self->window, NULL, mask, &attrib);
+ self->lgripbottom = createWindow(self->window, NULL, mask, &attrib);
+ self->rgripright = createWindow(self->window, NULL, mask, &attrib);
+ self->rgriptop = createWindow(self->window, NULL, mask, &attrib);
+ self->rgripbottom = createWindow(self->window, NULL, mask, &attrib);
+
+ self->focused = FALSE;
+
+ /* the other stuff is shown based on decor settings */
+ XMapWindow(obt_display, self->label);
+ XMapWindow(obt_display, self->backback);
+ XMapWindow(obt_display, self->backfront);
+
+ self->max_press = self->close_press = self->desk_press =
+ self->iconify_press = self->shade_press = FALSE;
+ self->max_hover = self->close_hover = self->desk_hover =
+ self->iconify_hover = self->shade_hover = FALSE;
+
+ /* make sure the size will be different the first time, so the extent hints
+ will be set */
+ STRUT_SET(self->oldsize, -1, -1, -1, -1);
+
+ set_theme_statics(self);
+
+ return self;
+}
+
+static void set_theme_statics(ObFrame *self)
+{
+ /* set colors/appearance/sizes for stuff that doesn't change */
+ XResizeWindow(obt_display, self->max,
+ ob_rr_theme->button_size, ob_rr_theme->button_size);
+ XResizeWindow(obt_display, self->iconify,
+ ob_rr_theme->button_size, ob_rr_theme->button_size);
+ XResizeWindow(obt_display, self->icon,
+ ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
+ XResizeWindow(obt_display, self->close,
+ ob_rr_theme->button_size, ob_rr_theme->button_size);
+ XResizeWindow(obt_display, self->desk,
+ ob_rr_theme->button_size, ob_rr_theme->button_size);
+ XResizeWindow(obt_display, self->shade,
+ ob_rr_theme->button_size, ob_rr_theme->button_size);
+ XResizeWindow(obt_display, self->tltresize,
+ ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
+ XResizeWindow(obt_display, self->trtresize,
+ ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
+ XResizeWindow(obt_display, self->tllresize,
+ ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
+ XResizeWindow(obt_display, self->trrresize,
+ ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
+}
+
+static void free_theme_statics(ObFrame *self)
+{
+}
+
+void frame_free(ObFrame *self)
+{
+ free_theme_statics(self);
+
+ XDestroyWindow(obt_display, self->window);
+ if (self->colormap)
+ XFreeColormap(obt_display, self->colormap);
+
+ g_slice_free(ObFrame, self);
+}
+
+void frame_show(ObFrame *self)
+{
+ if (!self->visible) {
+ self->visible = TRUE;
+ framerender_frame(self);
+ /* Grab the server to make sure that the frame window is mapped before
+ the client gets its MapNotify, i.e. to make sure the client is
+ _visible_ when it gets MapNotify. */
+ grab_server(TRUE);
+ XMapWindow(obt_display, self->client->window);
+ XMapWindow(obt_display, self->window);
+ grab_server(FALSE);
+ }
+}
+
+void frame_hide(ObFrame *self)
+{
+ if (self->visible) {
+ self->visible = FALSE;
+ if (!frame_iconify_animating(self))
+ XUnmapWindow(obt_display, self->window);
+ /* we unmap the client itself so that we can get MapRequest
+ events, and because the ICCCM tells us to! */
+ XUnmapWindow(obt_display, self->client->window);
+ self->client->ignore_unmaps += 1;
+ }
+}
+
+void frame_adjust_theme(ObFrame *self)
+{
+ free_theme_statics(self);
+ set_theme_statics(self);
+}
+
+#ifdef SHAPE
+void frame_adjust_shape_kind(ObFrame *self, int kind)
+{
+ gint num;
+ XRectangle xrect[2];
+ gboolean shaped;
+
+ shaped = (kind == ShapeBounding && self->client->shaped);
+#ifdef ShapeInput
+ shaped |= (kind == ShapeInput && self->client->shaped_input);
+#endif
+
+ if (!shaped) {
+ /* clear the shape on the frame window */
+ XShapeCombineMask(obt_display, self->window, kind,
+ self->size.left,
+ self->size.top,
+ None, ShapeSet);
+ } else {
+ /* make the frame's shape match the clients */
+ XShapeCombineShape(obt_display, self->window, kind,
+ self->size.left,
+ self->size.top,
+ self->client->window,
+ kind, ShapeSet);
+
+ num = 0;
+ if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
+ xrect[0].x = 0;
+ xrect[0].y = 0;
+ xrect[0].width = self->area.width;
+ xrect[0].height = self->size.top;
+ ++num;
+ }
+
+ if (self->decorations & OB_FRAME_DECOR_HANDLE &&
+ ob_rr_theme->handle_height > 0)
+ {
+ xrect[1].x = 0;
+ xrect[1].y = FRAME_HANDLE_Y(self);
+ xrect[1].width = self->area.width;
+ xrect[1].height = ob_rr_theme->handle_height +
+ self->bwidth * 2;
+ ++num;
+ }
+
+ XShapeCombineRectangles(obt_display, self->window,
+ ShapeBounding, 0, 0, xrect, num,
+ ShapeUnion, Unsorted);
+ }
+}
+#endif
+
+void frame_adjust_shape(ObFrame *self)
+{
+#ifdef SHAPE
+ frame_adjust_shape_kind(self, ShapeBounding);
+#ifdef ShapeInput
+ frame_adjust_shape_kind(self, ShapeInput);
+#endif
+#endif
+}
+
+void frame_adjust_area(ObFrame *self, gboolean moved,
+ gboolean resized, gboolean fake)
+{
+ if (resized) {
+ /* do this before changing the frame's status like max_horz max_vert */
+ frame_adjust_cursors(self);
+
+ self->functions = self->client->functions;
+ self->decorations = self->client->decorations;
+ self->max_horz = self->client->max_horz;
+ self->max_vert = self->client->max_vert;
+ self->shaded = self->client->shaded;
+
+ if (self->decorations & OB_FRAME_DECOR_BORDER)
+ self->bwidth = self->client->undecorated ?
+ ob_rr_theme->ubwidth : ob_rr_theme->fbwidth;
+ else
+ self->bwidth = 0;
+
+ if (self->decorations & OB_FRAME_DECOR_BORDER &&
+ !self->client->undecorated)
+ {
+ self->cbwidth_l = self->cbwidth_r = ob_rr_theme->cbwidthx;
+ self->cbwidth_t = self->cbwidth_b = ob_rr_theme->cbwidthy;
+ } else
+ self->cbwidth_l = self->cbwidth_t =
+ self->cbwidth_r = self->cbwidth_b = 0;
+
+ if (self->max_horz) {
+ self->cbwidth_l = self->cbwidth_r = 0;
+ self->width = self->client->area.width;
+ if (self->max_vert)
+ self->cbwidth_b = 0;
+ } else
+ self->width = self->client->area.width +
+ self->cbwidth_l + self->cbwidth_r;
+
+ /* some elements are sized based of the width, so don't let them have
+ negative values */
+ self->width = MAX(self->width,
+ (ob_rr_theme->grip_width + self->bwidth) * 2 + 1);
+
+ STRUT_SET(self->size,
+ self->cbwidth_l + (!self->max_horz ? self->bwidth : 0),
+ self->cbwidth_t + self->bwidth,
+ 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;
+ if (self->decorations & OB_FRAME_DECOR_HANDLE &&
+ ob_rr_theme->handle_height > 0)
+ {
+ self->size.bottom += ob_rr_theme->handle_height + self->bwidth;
+ }
+
+ /* position/size and map/unmap all the windows */
+
+ if (!fake) {
+ gint innercornerheight =
+ ob_rr_theme->grip_width - self->size.bottom;
+
+ if (self->cbwidth_l) {
+ XMoveResizeWindow(obt_display, self->innerleft,
+ self->size.left - self->cbwidth_l,
+ self->size.top,
+ self->cbwidth_l, self->client->area.height);
+
+ XMapWindow(obt_display, self->innerleft);
+ } else
+ XUnmapWindow(obt_display, self->innerleft);
+
+ if (self->cbwidth_l && innercornerheight > 0) {
+ XMoveResizeWindow(obt_display, self->innerbll,
+ 0,
+ self->client->area.height -
+ (ob_rr_theme->grip_width -
+ self->size.bottom),
+ self->cbwidth_l,
+ ob_rr_theme->grip_width - self->size.bottom);
+
+ XMapWindow(obt_display, self->innerbll);
+ } else
+ XUnmapWindow(obt_display, self->innerbll);
+
+ if (self->cbwidth_r) {
+ XMoveResizeWindow(obt_display, self->innerright,
+ self->size.left + self->client->area.width,
+ self->size.top,
+ self->cbwidth_r, self->client->area.height);
+
+ XMapWindow(obt_display, self->innerright);
+ } else
+ XUnmapWindow(obt_display, self->innerright);
+
+ if (self->cbwidth_r && innercornerheight > 0) {
+ XMoveResizeWindow(obt_display, self->innerbrr,
+ 0,
+ self->client->area.height -
+ (ob_rr_theme->grip_width -
+ self->size.bottom),
+ self->cbwidth_r,
+ ob_rr_theme->grip_width - self->size.bottom);
+
+ XMapWindow(obt_display, self->innerbrr);
+ } else
+ XUnmapWindow(obt_display, self->innerbrr);
+
+ if (self->cbwidth_t) {
+ XMoveResizeWindow(obt_display, self->innertop,
+ self->size.left - self->cbwidth_l,
+ self->size.top - self->cbwidth_t,
+ self->client->area.width +
+ self->cbwidth_l + self->cbwidth_r,
+ self->cbwidth_t);
+
+ XMapWindow(obt_display, self->innertop);
+ } else
+ XUnmapWindow(obt_display, self->innertop);
+
+ if (self->cbwidth_b) {
+ XMoveResizeWindow(obt_display, self->innerbottom,
+ self->size.left - self->cbwidth_l,
+ self->size.top + self->client->area.height,
+ self->client->area.width +
+ self->cbwidth_l + self->cbwidth_r,
+ self->cbwidth_b);
+
+ XMoveResizeWindow(obt_display, self->innerblb,
+ 0, 0,
+ ob_rr_theme->grip_width + self->bwidth,
+ self->cbwidth_b);
+ XMoveResizeWindow(obt_display, self->innerbrb,
+ self->client->area.width +
+ self->cbwidth_l + self->cbwidth_r -
+ (ob_rr_theme->grip_width + self->bwidth),
+ 0,
+ ob_rr_theme->grip_width + self->bwidth,
+ self->cbwidth_b);
+
+ XMapWindow(obt_display, self->innerbottom);
+ XMapWindow(obt_display, self->innerblb);
+ XMapWindow(obt_display, self->innerbrb);
+ } else {
+ XUnmapWindow(obt_display, self->innerbottom);
+ XUnmapWindow(obt_display, self->innerblb);
+ XUnmapWindow(obt_display, self->innerbrb);
+ }
+
+ if (self->bwidth) {
+ gint titlesides;
+
+ /* height of titleleft and titleright */
+ titlesides = (!self->max_horz ? ob_rr_theme->grip_width : 0);
+
+ XMoveResizeWindow(obt_display, self->titletop,
+ ob_rr_theme->grip_width + self->bwidth, 0,
+ /* width + bwidth*2 - bwidth*2 - grips*2 */
+ self->width - ob_rr_theme->grip_width * 2,
+ self->bwidth);
+ XMoveResizeWindow(obt_display, self->titletopleft,
+ 0, 0,
+ ob_rr_theme->grip_width + self->bwidth,
+ self->bwidth);
+ XMoveResizeWindow(obt_display, self->titletopright,
+ self->client->area.width +
+ self->size.left + self->size.right -
+ ob_rr_theme->grip_width - self->bwidth,
+ 0,
+ ob_rr_theme->grip_width + self->bwidth,
+ self->bwidth);
+
+ if (titlesides > 0) {
+ XMoveResizeWindow(obt_display, self->titleleft,
+ 0, self->bwidth,
+ self->bwidth,
+ titlesides);
+ XMoveResizeWindow(obt_display, self->titleright,
+ self->client->area.width +
+ self->size.left + self->size.right -
+ self->bwidth,
+ self->bwidth,
+ self->bwidth,
+ titlesides);
+
+ XMapWindow(obt_display, self->titleleft);
+ XMapWindow(obt_display, self->titleright);
+ } else {
+ XUnmapWindow(obt_display, self->titleleft);
+ XUnmapWindow(obt_display, self->titleright);
+ }
+
+ XMapWindow(obt_display, self->titletop);
+ XMapWindow(obt_display, self->titletopleft);
+ XMapWindow(obt_display, self->titletopright);
+
+ if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
+ XMoveResizeWindow(obt_display, self->titlebottom,
+ (self->max_horz ? 0 : self->bwidth),
+ ob_rr_theme->title_height + self->bwidth,
+ self->width,
+ self->bwidth);
+
+ XMapWindow(obt_display, self->titlebottom);
+ } else
+ XUnmapWindow(obt_display, self->titlebottom);
+ } else {
+ XUnmapWindow(obt_display, self->titlebottom);
+
+ XUnmapWindow(obt_display, self->titletop);
+ XUnmapWindow(obt_display, self->titletopleft);
+ XUnmapWindow(obt_display, self->titletopright);
+ XUnmapWindow(obt_display, self->titleleft);
+ XUnmapWindow(obt_display, self->titleright);
+ }
+
+ if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
+ XMoveResizeWindow(obt_display, self->title,
+ (self->max_horz ? 0 : self->bwidth),
+ self->bwidth,
+ self->width, ob_rr_theme->title_height);
+
+ XMapWindow(obt_display, self->title);
+
+ if (self->decorations & OB_FRAME_DECOR_GRIPS) {
+ XMoveResizeWindow(obt_display, self->topresize,
+ ob_rr_theme->grip_width,
+ 0,
+ self->width - ob_rr_theme->grip_width *2,
+ ob_rr_theme->paddingy + 1);
+
+ XMoveWindow(obt_display, self->tltresize, 0, 0);
+ XMoveWindow(obt_display, self->tllresize, 0, 0);
+ XMoveWindow(obt_display, self->trtresize,
+ self->width - ob_rr_theme->grip_width, 0);
+ XMoveWindow(obt_display, self->trrresize,
+ self->width - ob_rr_theme->paddingx - 1, 0);
+
+ XMapWindow(obt_display, self->topresize);
+ XMapWindow(obt_display, self->tltresize);
+ XMapWindow(obt_display, self->tllresize);
+ XMapWindow(obt_display, self->trtresize);
+ XMapWindow(obt_display, self->trrresize);
+ } else {
+ XUnmapWindow(obt_display, self->topresize);
+ XUnmapWindow(obt_display, self->tltresize);
+ XUnmapWindow(obt_display, self->tllresize);
+ XUnmapWindow(obt_display, self->trtresize);
+ XUnmapWindow(obt_display, self->trrresize);
+ }
+ } else
+ XUnmapWindow(obt_display, self->title);
+ }
+
+ if ((self->decorations & OB_FRAME_DECOR_TITLEBAR))
+ /* layout the title bar elements */
+ layout_title(self);
+
+ if (!fake) {
+ gint sidebwidth = self->max_horz ? 0 : self->bwidth;
+
+ if (self->bwidth && self->size.bottom) {
+ XMoveResizeWindow(obt_display, self->handlebottom,
+ ob_rr_theme->grip_width +
+ self->bwidth + sidebwidth,
+ self->size.top + self->client->area.height +
+ self->size.bottom - self->bwidth,
+ self->width - (ob_rr_theme->grip_width +
+ sidebwidth) * 2,
+ self->bwidth);
+
+
+ if (sidebwidth) {
+ XMoveResizeWindow(obt_display, self->lgripleft,
+ 0,
+ self->size.top +
+ self->client->area.height +
+ self->size.bottom -
+ (!self->max_horz ?
+ ob_rr_theme->grip_width :
+ self->size.bottom - self->cbwidth_b),
+ self->bwidth,
+ (!self->max_horz ?
+ ob_rr_theme->grip_width :
+ self->size.bottom - self->cbwidth_b));
+ XMoveResizeWindow(obt_display, self->rgripright,
+ self->size.left +
+ self->client->area.width +
+ self->size.right - self->bwidth,
+ self->size.top +
+ self->client->area.height +
+ self->size.bottom -
+ (!self->max_horz ?
+ ob_rr_theme->grip_width :
+ self->size.bottom - self->cbwidth_b),
+ self->bwidth,
+ (!self->max_horz ?
+ ob_rr_theme->grip_width :
+ self->size.bottom - self->cbwidth_b));
+
+ XMapWindow(obt_display, self->lgripleft);
+ XMapWindow(obt_display, self->rgripright);
+ } else {
+ XUnmapWindow(obt_display, self->lgripleft);
+ XUnmapWindow(obt_display, self->rgripright);
+ }
+
+ XMoveResizeWindow(obt_display, self->lgripbottom,
+ sidebwidth,
+ self->size.top + self->client->area.height +
+ self->size.bottom - self->bwidth,
+ ob_rr_theme->grip_width + self->bwidth,
+ self->bwidth);
+ XMoveResizeWindow(obt_display, self->rgripbottom,
+ self->size.left + self->client->area.width +
+ self->size.right - self->bwidth - sidebwidth-
+ ob_rr_theme->grip_width,
+ self->size.top + self->client->area.height +
+ self->size.bottom - self->bwidth,
+ ob_rr_theme->grip_width + self->bwidth,
+ self->bwidth);
+
+ XMapWindow(obt_display, self->handlebottom);
+ XMapWindow(obt_display, self->lgripbottom);
+ XMapWindow(obt_display, self->rgripbottom);
+
+ if (self->decorations & OB_FRAME_DECOR_HANDLE &&
+ ob_rr_theme->handle_height > 0)
+ {
+ XMoveResizeWindow(obt_display, self->handletop,
+ ob_rr_theme->grip_width +
+ self->bwidth + sidebwidth,
+ FRAME_HANDLE_Y(self),
+ self->width - (ob_rr_theme->grip_width +
+ sidebwidth) * 2,
+ self->bwidth);
+ XMapWindow(obt_display, self->handletop);
+
+ if (self->decorations & OB_FRAME_DECOR_GRIPS) {
+ XMoveResizeWindow(obt_display, self->handleleft,
+ ob_rr_theme->grip_width,
+ 0,
+ self->bwidth,
+ ob_rr_theme->handle_height);
+ XMoveResizeWindow(obt_display, self->handleright,
+ self->width -
+ ob_rr_theme->grip_width -
+ self->bwidth,
+ 0,
+ self->bwidth,
+ ob_rr_theme->handle_height);
+
+ XMoveResizeWindow(obt_display, self->lgriptop,
+ sidebwidth,
+ FRAME_HANDLE_Y(self),
+ ob_rr_theme->grip_width +
+ self->bwidth,
+ self->bwidth);
+ XMoveResizeWindow(obt_display, self->rgriptop,
+ self->size.left +
+ self->client->area.width +
+ self->size.right - self->bwidth -
+ sidebwidth - ob_rr_theme->grip_width,
+ FRAME_HANDLE_Y(self),
+ ob_rr_theme->grip_width +
+ self->bwidth,
+ self->bwidth);
+
+ XMapWindow(obt_display, self->handleleft);
+ XMapWindow(obt_display, self->handleright);
+ XMapWindow(obt_display, self->lgriptop);
+ XMapWindow(obt_display, self->rgriptop);
+ } else {
+ XUnmapWindow(obt_display, self->handleleft);
+ XUnmapWindow(obt_display, self->handleright);
+ XUnmapWindow(obt_display, self->lgriptop);
+ XUnmapWindow(obt_display, self->rgriptop);
+ }
+ } else {
+ XUnmapWindow(obt_display, self->handleleft);
+ XUnmapWindow(obt_display, self->handleright);
+ XUnmapWindow(obt_display, self->lgriptop);
+ XUnmapWindow(obt_display, self->rgriptop);
+
+ XUnmapWindow(obt_display, self->handletop);
+ }
+ } else {
+ XUnmapWindow(obt_display, self->handleleft);
+ XUnmapWindow(obt_display, self->handleright);
+ XUnmapWindow(obt_display, self->lgriptop);
+ XUnmapWindow(obt_display, self->rgriptop);
+
+ XUnmapWindow(obt_display, self->handletop);
+
+ XUnmapWindow(obt_display, self->handlebottom);
+ XUnmapWindow(obt_display, self->lgripleft);
+ XUnmapWindow(obt_display, self->rgripright);
+ XUnmapWindow(obt_display, self->lgripbottom);
+ XUnmapWindow(obt_display, self->rgripbottom);
+ }
+
+ if (self->decorations & OB_FRAME_DECOR_HANDLE &&
+ ob_rr_theme->handle_height > 0)
+ {
+ XMoveResizeWindow(obt_display, self->handle,
+ sidebwidth,
+ FRAME_HANDLE_Y(self) + self->bwidth,
+ self->width, ob_rr_theme->handle_height);
+ XMapWindow(obt_display, self->handle);
+
+ if (self->decorations & OB_FRAME_DECOR_GRIPS) {
+ XMoveResizeWindow(obt_display, self->lgrip,
+ 0, 0,
+ ob_rr_theme->grip_width,
+ ob_rr_theme->handle_height);
+ XMoveResizeWindow(obt_display, self->rgrip,
+ self->width - ob_rr_theme->grip_width,
+ 0,
+ ob_rr_theme->grip_width,
+ ob_rr_theme->handle_height);
+
+ XMapWindow(obt_display, self->lgrip);
+ XMapWindow(obt_display, self->rgrip);
+ } else {
+ XUnmapWindow(obt_display, self->lgrip);
+ XUnmapWindow(obt_display, self->rgrip);
+ }
+ } else {
+ XUnmapWindow(obt_display, self->lgrip);
+ XUnmapWindow(obt_display, self->rgrip);
+
+ XUnmapWindow(obt_display, self->handle);
+ }
+
+ if (self->bwidth && !self->max_horz &&
+ (self->client->area.height + self->size.top +
+ self->size.bottom) > ob_rr_theme->grip_width * 2)
+ {
+ XMoveResizeWindow(obt_display, self->left,
+ 0,
+ self->bwidth + ob_rr_theme->grip_width,
+ self->bwidth,
+ self->client->area.height +
+ self->size.top + self->size.bottom -
+ ob_rr_theme->grip_width * 2);
+
+ XMapWindow(obt_display, self->left);
+ } else
+ XUnmapWindow(obt_display, self->left);
+
+ if (self->bwidth && !self->max_horz &&
+ (self->client->area.height + self->size.top +
+ self->size.bottom) > ob_rr_theme->grip_width * 2)
+ {
+ XMoveResizeWindow(obt_display, self->right,
+ self->client->area.width + self->cbwidth_l +
+ self->cbwidth_r + self->bwidth,
+ self->bwidth + ob_rr_theme->grip_width,
+ self->bwidth,
+ self->client->area.height +
+ self->size.top + self->size.bottom -
+ ob_rr_theme->grip_width * 2);
+
+ XMapWindow(obt_display, self->right);
+ } else
+ XUnmapWindow(obt_display, self->right);
+
+ XMoveResizeWindow(obt_display, self->backback,
+ self->size.left, self->size.top,
+ self->client->area.width,
+ self->client->area.height);
+ }
+ }
+
+ /* shading can change without being moved or resized */
+ RECT_SET_SIZE(self->area,
+ self->client->area.width +
+ self->size.left + self->size.right,
+ (self->client->shaded ?
+ ob_rr_theme->title_height + self->bwidth * 2:
+ self->client->area.height +
+ self->size.top + self->size.bottom));
+
+ if ((moved || resized) && !fake) {
+ /* find the new coordinates, done after setting the frame.size, for
+ frame_client_gravity. */
+ self->area.x = self->client->area.x;
+ self->area.y = self->client->area.y;
+ frame_client_gravity(self, &self->area.x, &self->area.y);
+ }
+
+ if (!fake) {
+ if (!frame_iconify_animating(self))
+ /* move and resize the top level frame.
+ shading can change without being moved or resized.
+
+ but don't do this during an iconify animation. it will be
+ reflected afterwards.
+ */
+ XMoveResizeWindow(obt_display, self->window,
+ self->area.x,
+ self->area.y,
+ self->area.width,
+ self->area.height);
+
+ /* when the client has StaticGravity, it likes to move around.
+ also this correctly positions the client when it maps.
+ this also needs to be run when the frame's decorations sizes change!
+ */
+ XMoveWindow(obt_display, self->client->window,
+ self->size.left, self->size.top);
+
+ if (resized) {
+ self->need_render = TRUE;
+ framerender_frame(self);
+ frame_adjust_shape(self);
+ }
+
+ if (!STRUT_EQUAL(self->size, self->oldsize)) {
+ gulong vals[4];
+ vals[0] = self->size.left;
+ vals[1] = self->size.right;
+ vals[2] = self->size.top;
+ vals[3] = self->size.bottom;
+ OBT_PROP_SETA32(self->client->window, NET_FRAME_EXTENTS,
+ CARDINAL, vals, 4);
+ OBT_PROP_SETA32(self->client->window, KDE_NET_WM_FRAME_STRUT,
+ CARDINAL, vals, 4);
+ self->oldsize = self->size;
+ }
+
+ /* if this occurs while we are focus cycling, the indicator needs to
+ match the changes */
+ if (focus_cycle_target == self->client)
+ focus_cycle_update_indicator(self->client);
+ }
+ if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR) &&
+ self->label_width)
+ {
+ XResizeWindow(obt_display, self->label, self->label_width,
+ ob_rr_theme->label_height);
+ }
+}
+
+static void frame_adjust_cursors(ObFrame *self)
+{
+ if ((self->functions & OB_CLIENT_FUNC_RESIZE) !=
+ (self->client->functions & OB_CLIENT_FUNC_RESIZE) ||
+ self->max_horz != self->client->max_horz ||
+ self->max_vert != self->client->max_vert ||
+ self->shaded != self->client->shaded)
+ {
+ gboolean r = (self->client->functions & OB_CLIENT_FUNC_RESIZE) &&
+ !(self->client->max_horz && self->client->max_vert);
+ gboolean topbot = !self->client->max_vert;
+ gboolean sh = self->client->shaded;
+ XSetWindowAttributes a;
+
+ /* these ones turn off when max vert, and some when shaded */
+ a.cursor = ob_cursor(r && topbot && !sh ?
+ OB_CURSOR_NORTH : OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->topresize, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->titletop, CWCursor, &a);
+ a.cursor = ob_cursor(r && topbot ? OB_CURSOR_SOUTH : OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->handle, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->handletop, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->handlebottom, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerbottom, CWCursor, &a);
+
+ /* these ones change when shaded */
+ a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_WEST : OB_CURSOR_NORTHWEST) :
+ OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->titleleft, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->tltresize, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->tllresize, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->titletopleft, CWCursor, &a);
+ a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_EAST : OB_CURSOR_NORTHEAST) :
+ OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->titleright, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->trtresize, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->trrresize, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->titletopright, CWCursor,&a);
+
+ /* these ones are pretty static */
+ a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->left, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerleft, CWCursor, &a);
+ a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->right, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerright, CWCursor, &a);
+ a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->lgrip, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->handleleft, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->lgripleft, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->lgriptop, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->lgripbottom, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerbll, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerblb, CWCursor, &a);
+ a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE);
+ XChangeWindowAttributes(obt_display, self->rgrip, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->handleright, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->rgripright, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->rgriptop, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->rgripbottom, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerbrr, CWCursor, &a);
+ XChangeWindowAttributes(obt_display, self->innerbrb, CWCursor, &a);
+ }
+}
+
+void frame_adjust_client_area(ObFrame *self)
+{
+ /* adjust the window which is there to prevent flashing on unmap */
+ XMoveResizeWindow(obt_display, self->backfront, 0, 0,
+ self->client->area.width,
+ self->client->area.height);
+}
+
+void frame_adjust_state(ObFrame *self)
+{
+ self->need_render = TRUE;
+ framerender_frame(self);
+}
+
+void frame_adjust_focus(ObFrame *self, gboolean hilite)
+{
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Frame for 0x%x has focus: %d",
+ self->client->window, hilite);
+ self->focused = hilite;
+ self->need_render = TRUE;
+ framerender_frame(self);
+ XFlush(obt_display);
+}
+
+void frame_adjust_title(ObFrame *self)
+{
+ self->need_render = TRUE;
+ framerender_frame(self);
+}
+
+void frame_adjust_icon(ObFrame *self)
+{
+ self->need_render = TRUE;
+ framerender_frame(self);
+}
+
+void frame_grab_client(ObFrame *self)
+{
+ /* DO NOT map the client window here. we used to do that, but it is bogus.
+ we need to set up the client's dimensions and everything before we
+ send a mapnotify or we create race conditions.
+ */
+
+ /* reparent the client to the frame */
+ XReparentWindow(obt_display, self->client->window, self->window, 0, 0);
+
+ /*
+ When reparenting the client window, it is usually not mapped yet, since
+ this occurs from a MapRequest. However, in the case where Openbox is
+ starting up, the window is already mapped, so we'll see an unmap event
+ for it.
+ */
+ if (ob_state() == OB_STATE_STARTING)
+ ++self->client->ignore_unmaps;
+
+ /* select the event mask on the client's parent (to receive config/map
+ req's) the ButtonPress is to catch clicks on the client border */
+ XSelectInput(obt_display, self->window, FRAME_EVENTMASK);
+
+ /* set all the windows for the frame in the window_map */
+ window_add(&self->window, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->backback, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->backfront, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerleft, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innertop, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerright, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerbottom, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerblb, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerbll, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerbrb, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->innerbrr, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->title, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->label, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->max, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->close, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->desk, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->shade, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->icon, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->iconify, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->handle, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->lgrip, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->rgrip, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->topresize, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->tltresize, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->tllresize, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->trtresize, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->trrresize, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->left, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->right, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->titleleft, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->titletop, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->titletopleft, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->titletopright, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->titleright, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->titlebottom, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->handleleft, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->handletop, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->handleright, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->handlebottom, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->lgripleft, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->lgriptop, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->lgripbottom, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->rgripright, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->rgriptop, CLIENT_AS_WINDOW(self->client));
+ window_add(&self->rgripbottom, CLIENT_AS_WINDOW(self->client));
+}
+
+static gboolean find_reparent(XEvent *e, gpointer data)
+{
+ const ObFrame *self = data;
+
+ /* Find ReparentNotify events for the window that aren't being reparented into the
+ frame, thus the client reparenting itself off the frame. */
+ return e->type == ReparentNotify && e->xreparent.window == self->client->window &&
+ e->xreparent.parent != self->window;
+}
+
+void frame_release_client(ObFrame *self)
+{
+ /* if there was any animation going on, kill it */
+ if (self->iconify_animation_timer)
+ g_source_remove(self->iconify_animation_timer);
+
+ /* check if the app has already reparented its window away */
+ if (!xqueue_exists_local(find_reparent, self)) {
+ /* according to the ICCCM - if the client doesn't reparent itself,
+ then we will reparent the window to root for them */
+ XReparentWindow(obt_display, self->client->window, obt_root(ob_screen),
+ self->client->area.x, self->client->area.y);
+ }
+
+ /* remove all the windows for the frame from the window_map */
+ window_remove(self->window);
+ window_remove(self->backback);
+ window_remove(self->backfront);
+ window_remove(self->innerleft);
+ window_remove(self->innertop);
+ window_remove(self->innerright);
+ window_remove(self->innerbottom);
+ window_remove(self->innerblb);
+ window_remove(self->innerbll);
+ window_remove(self->innerbrb);
+ window_remove(self->innerbrr);
+ window_remove(self->title);
+ window_remove(self->label);
+ window_remove(self->max);
+ window_remove(self->close);
+ window_remove(self->desk);
+ window_remove(self->shade);
+ window_remove(self->icon);
+ window_remove(self->iconify);
+ window_remove(self->handle);
+ window_remove(self->lgrip);
+ window_remove(self->rgrip);
+ window_remove(self->topresize);
+ window_remove(self->tltresize);
+ window_remove(self->tllresize);
+ window_remove(self->trtresize);
+ window_remove(self->trrresize);
+ window_remove(self->left);
+ window_remove(self->right);
+ window_remove(self->titleleft);
+ window_remove(self->titletop);
+ window_remove(self->titletopleft);
+ window_remove(self->titletopright);
+ window_remove(self->titleright);
+ window_remove(self->titlebottom);
+ window_remove(self->handleleft);
+ window_remove(self->handletop);
+ window_remove(self->handleright);
+ window_remove(self->handlebottom);
+ window_remove(self->lgripleft);
+ window_remove(self->lgriptop);
+ window_remove(self->lgripbottom);
+ window_remove(self->rgripright);
+ window_remove(self->rgriptop);
+ window_remove(self->rgripbottom);
+
+ if (self->flash_timer) g_source_remove(self->flash_timer);
+}
+
+/* is there anything present between us and the label? */
+static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) {
+ for (; *lc != '\0' && lc >= config_title_layout; lc += dir) {
+ if (*lc == ' ') continue; /* it was invalid */
+ if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON)
+ return TRUE;
+ if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
+ return TRUE;
+ if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE)
+ return TRUE;
+ if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY)
+ return TRUE;
+ if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE)
+ return TRUE;
+ if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE)
+ return TRUE;
+ if (*lc == 'L') return FALSE;
+ }
+ return FALSE;
+}
+
+static void place_button(ObFrame *self, const char *lc, gint bwidth,
+ gint left, gint i,
+ gint *x, gint *button_on, gint *button_x)
+{
+ if (!(*button_on = is_button_present(self, lc, i)))
+ return;
+
+ self->label_width -= bwidth;
+ if (i > 0)
+ *button_x = *x;
+ *x += i * bwidth;
+ if (i < 0) {
+ if (self->label_x <= left || *x > self->label_x) {
+ *button_x = *x;
+ } else {
+ /* the button would have been drawn on top of another button */
+ *button_on = FALSE;
+ self->label_width += bwidth;
+ }
+ }
+}
+
+static void layout_title(ObFrame *self)
+{
+ gchar *lc;
+ gint i;
+
+ const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
+ /* position of the leftmost button */
+ const gint left = ob_rr_theme->paddingx + 1;
+ /* position of the rightmost button */
+ const gint right = self->width;
+
+ /* turn them all off */
+ self->icon_on = self->desk_on = self->shade_on = self->iconify_on =
+ self->max_on = self->close_on = self->label_on = FALSE;
+ self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
+ self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE;
+
+ /* figure out what's being shown, find each element's position, and the
+ width of the label
+
+ do the ones before the label, then after the label,
+ i will be +1 the first time through when working to the left,
+ and -1 the second time through when working to the right */
+ for (i = 1; i >= -1; i-=2) {
+ gint x;
+ ObFrameContext *firstcon;
+
+ if (i > 0) {
+ x = left;
+ lc = config_title_layout;
+ firstcon = &self->leftmost;
+ } else {
+ x = right;
+ lc = config_title_layout + strlen(config_title_layout)-1;
+ firstcon = &self->rightmost;
+ }
+
+ /* stop at the end of the string (or the label, which calls break) */
+ for (; *lc != '\0' && lc >= config_title_layout; lc+=i) {
+ if (*lc == 'L') {
+ if (i > 0) {
+ self->label_on = TRUE;
+ self->label_x = x;
+ }
+ break; /* break the for loop, do other side of label */
+ } else if (*lc == 'N') {
+ if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON;
+ /* icon is bigger than buttons */
+ place_button(self, lc, bwidth + 2, left, i, &x, &self->icon_on, &self->icon_x);
+ } else if (*lc == 'D') {
+ if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS;
+ place_button(self, lc, bwidth, left, i, &x, &self->desk_on, &self->desk_x);
+ } else if (*lc == 'S') {
+ if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE;
+ place_button(self, lc, bwidth, left, i, &x, &self->shade_on, &self->shade_x);
+ } else if (*lc == 'I') {
+ if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY;
+ place_button(self, lc, bwidth, left, i, &x, &self->iconify_on, &self->iconify_x);
+ } else if (*lc == 'M') {
+ if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE;
+ place_button(self, lc, bwidth, left, i, &x, &self->max_on, &self->max_x);
+ } else if (*lc == 'C') {
+ if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE;
+ place_button(self, lc, bwidth, left, i, &x, &self->close_on, &self->close_x);
+ } else
+ continue; /* don't set firstcon */
+ firstcon = NULL;
+ }
+ }
+
+ /* position and map the elements */
+ if (self->icon_on) {
+ XMapWindow(obt_display, self->icon);
+ XMoveWindow(obt_display, self->icon, self->icon_x,
+ ob_rr_theme->paddingy);
+ } else
+ XUnmapWindow(obt_display, self->icon);
+
+ if (self->desk_on) {
+ XMapWindow(obt_display, self->desk);
+ XMoveWindow(obt_display, self->desk, self->desk_x,
+ ob_rr_theme->paddingy + 1);
+ } else
+ XUnmapWindow(obt_display, self->desk);
+
+ if (self->shade_on) {
+ XMapWindow(obt_display, self->shade);
+ XMoveWindow(obt_display, self->shade, self->shade_x,
+ ob_rr_theme->paddingy + 1);
+ } else
+ XUnmapWindow(obt_display, self->shade);
+
+ if (self->iconify_on) {
+ XMapWindow(obt_display, self->iconify);
+ XMoveWindow(obt_display, self->iconify, self->iconify_x,
+ ob_rr_theme->paddingy + 1);
+ } else
+ XUnmapWindow(obt_display, self->iconify);
+
+ if (self->max_on) {
+ XMapWindow(obt_display, self->max);
+ XMoveWindow(obt_display, self->max, self->max_x,
+ ob_rr_theme->paddingy + 1);
+ } else
+ XUnmapWindow(obt_display, self->max);
+
+ if (self->close_on) {
+ XMapWindow(obt_display, self->close);
+ XMoveWindow(obt_display, self->close, self->close_x,
+ ob_rr_theme->paddingy + 1);
+ } else
+ XUnmapWindow(obt_display, self->close);
+
+ if (self->label_on && self->label_width > 0) {
+ XMapWindow(obt_display, self->label);
+ XMoveWindow(obt_display, self->label, self->label_x,
+ ob_rr_theme->paddingy);
+ } else
+ XUnmapWindow(obt_display, self->label);
+}
+
+gboolean frame_next_context_from_string(gchar *names, ObFrameContext *cx)
+{
+ gchar *p, *n;
+
+ if (!*names) /* empty string */
+ return FALSE;
+
+ /* find the first space */
+ for (p = names; *p; p = g_utf8_next_char(p)) {
+ const gunichar c = g_utf8_get_char(p);
+ if (g_unichar_isspace(c)) break;
+ }
+
+ if (p == names) {
+ /* leading spaces in the string */
+ n = g_utf8_next_char(names);
+ if (!frame_next_context_from_string(n, cx))
+ return FALSE;
+ } else {
+ n = p;
+ if (*p) {
+ /* delete the space with null zero(s) */
+ while (n < g_utf8_next_char(p))
+ *(n++) = '\0';
+ }
+
+ *cx = frame_context_from_string(names);
+
+ /* find the next non-space */
+ for (; *n; n = g_utf8_next_char(n)) {
+ const gunichar c = g_utf8_get_char(n);
+ if (!g_unichar_isspace(c)) break;
+ }
+ }
+
+ /* delete everything we just read (copy everything at n to the start of
+ the string */
+ for (p = names; *n; ++p, ++n)
+ *p = *n;
+ *p = *n;
+
+ return TRUE;
+}
+
+ObFrameContext frame_context_from_string(const gchar *name)
+{
+ if (!g_ascii_strcasecmp("Desktop", name))
+ return OB_FRAME_CONTEXT_DESKTOP;
+ else if (!g_ascii_strcasecmp("Root", name))
+ return OB_FRAME_CONTEXT_ROOT;
+ else if (!g_ascii_strcasecmp("Client", name))
+ return OB_FRAME_CONTEXT_CLIENT;
+ else if (!g_ascii_strcasecmp("Titlebar", name))
+ return OB_FRAME_CONTEXT_TITLEBAR;
+ else if (!g_ascii_strcasecmp("Frame", name))
+ return OB_FRAME_CONTEXT_FRAME;
+ else if (!g_ascii_strcasecmp("TLCorner", name))
+ return OB_FRAME_CONTEXT_TLCORNER;
+ else if (!g_ascii_strcasecmp("TRCorner", name))
+ return OB_FRAME_CONTEXT_TRCORNER;
+ else if (!g_ascii_strcasecmp("BLCorner", name))
+ return OB_FRAME_CONTEXT_BLCORNER;
+ else if (!g_ascii_strcasecmp("BRCorner", name))
+ return OB_FRAME_CONTEXT_BRCORNER;
+ else if (!g_ascii_strcasecmp("Top", name))
+ return OB_FRAME_CONTEXT_TOP;
+ else if (!g_ascii_strcasecmp("Bottom", name))
+ return OB_FRAME_CONTEXT_BOTTOM;
+ else if (!g_ascii_strcasecmp("Left", name))
+ return OB_FRAME_CONTEXT_LEFT;
+ else if (!g_ascii_strcasecmp("Right", name))
+ return OB_FRAME_CONTEXT_RIGHT;
+ else if (!g_ascii_strcasecmp("Maximize", name))
+ return OB_FRAME_CONTEXT_MAXIMIZE;
+ else if (!g_ascii_strcasecmp("AllDesktops", name))
+ return OB_FRAME_CONTEXT_ALLDESKTOPS;
+ else if (!g_ascii_strcasecmp("Shade", name))
+ return OB_FRAME_CONTEXT_SHADE;
+ else if (!g_ascii_strcasecmp("Iconify", name))
+ return OB_FRAME_CONTEXT_ICONIFY;
+ else if (!g_ascii_strcasecmp("Icon", name))
+ return OB_FRAME_CONTEXT_ICON;
+ else if (!g_ascii_strcasecmp("Close", name))
+ return OB_FRAME_CONTEXT_CLOSE;
+ else if (!g_ascii_strcasecmp("MoveResize", name))
+ return OB_FRAME_CONTEXT_MOVE_RESIZE;
+ else if (!g_ascii_strcasecmp("Dock", name))
+ return OB_FRAME_CONTEXT_DOCK;
+
+ return OB_FRAME_CONTEXT_NONE;
+}
+
+ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
+{
+ ObFrame *self;
+ ObWindow *obwin;
+
+ if (moveresize_in_progress)
+ return OB_FRAME_CONTEXT_MOVE_RESIZE;
+
+ if (win == obt_root(ob_screen))
+ return OB_FRAME_CONTEXT_ROOT;
+ if ((obwin = window_find(win))) {
+ if (WINDOW_IS_DOCK(obwin)) {
+ return OB_FRAME_CONTEXT_DOCK;
+ }
+ }
+ if (client == NULL) return OB_FRAME_CONTEXT_NONE;
+ if (win == client->window) {
+ /* conceptually, this is the desktop, as far as users are
+ concerned */
+ if (client->type == OB_CLIENT_TYPE_DESKTOP)
+ return OB_FRAME_CONTEXT_DESKTOP;
+ return OB_FRAME_CONTEXT_CLIENT;
+ }
+
+ self = client->frame;
+
+ /* when the user clicks in the corners of the titlebar and the client
+ is fully maximized, then treat it like they clicked in the
+ button that is there */
+ if (self->max_horz && self->max_vert &&
+ (win == self->title || win == self->titletop ||
+ win == self->titleleft || win == self->titletopleft ||
+ win == self->titleright || win == self->titletopright))
+ {
+ /* get the mouse coords in reference to the whole frame */
+ gint fx = x;
+ gint fy = y;
+
+ /* these windows are down a border width from the top of the frame */
+ if (win == self->title ||
+ win == self->titleleft || win == self->titleright)
+ fy += self->bwidth;
+
+ /* title is a border width in from the edge */
+ if (win == self->title)
+ fx += self->bwidth;
+ /* titletop is a bit to the right */
+ else if (win == self->titletop)
+ fx += ob_rr_theme->grip_width + self->bwidth;
+ /* titletopright is way to the right edge */
+ else if (win == self->titletopright)
+ fx += self->area.width - (ob_rr_theme->grip_width + self->bwidth);
+ /* titleright is even more way to the right edge */
+ else if (win == self->titleright)
+ fx += self->area.width - self->bwidth;
+
+ /* figure out if we're over the area that should be considered a
+ button */
+ if (fy < self->bwidth + ob_rr_theme->paddingy + 1 +
+ ob_rr_theme->button_size)
+ {
+ if (fx < (self->bwidth + ob_rr_theme->paddingx + 1 +
+ ob_rr_theme->button_size))
+ {
+ if (self->leftmost != OB_FRAME_CONTEXT_NONE)
+ return self->leftmost;
+ }
+ else if (fx >= (self->area.width -
+ (self->bwidth + ob_rr_theme->paddingx + 1 +
+ ob_rr_theme->button_size)))
+ {
+ if (self->rightmost != OB_FRAME_CONTEXT_NONE)
+ return self->rightmost;
+ }
+ }
+
+ /* there is no resizing maximized windows so make them the titlebar
+ context */
+ return OB_FRAME_CONTEXT_TITLEBAR;
+ }
+ else if (self->max_vert &&
+ (win == self->titletop || win == self->topresize))
+ /* can't resize vertically when max vert */
+ return OB_FRAME_CONTEXT_TITLEBAR;
+ else if (self->shaded &&
+ (win == self->titletop || win == self->topresize))
+ /* can't resize vertically when shaded */
+ return OB_FRAME_CONTEXT_TITLEBAR;
+
+ if (win == self->window) return OB_FRAME_CONTEXT_FRAME;
+ if (win == self->label) return OB_FRAME_CONTEXT_TITLEBAR;
+ if (win == self->handle) return OB_FRAME_CONTEXT_BOTTOM;
+ if (win == self->handletop) return OB_FRAME_CONTEXT_BOTTOM;
+ if (win == self->handlebottom) return OB_FRAME_CONTEXT_BOTTOM;
+ if (win == self->handleleft) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->lgrip) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->lgripleft) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->lgriptop) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->lgripbottom) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->handleright) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->rgrip) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->rgripright) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->rgriptop) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->rgripbottom) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->title) return OB_FRAME_CONTEXT_TITLEBAR;
+ if (win == self->titlebottom) return OB_FRAME_CONTEXT_TITLEBAR;
+ if (win == self->titleleft) return OB_FRAME_CONTEXT_TLCORNER;
+ if (win == self->titletopleft) return OB_FRAME_CONTEXT_TLCORNER;
+ if (win == self->titleright) return OB_FRAME_CONTEXT_TRCORNER;
+ if (win == self->titletopright) return OB_FRAME_CONTEXT_TRCORNER;
+ if (win == self->titletop) return OB_FRAME_CONTEXT_TOP;
+ if (win == self->topresize) return OB_FRAME_CONTEXT_TOP;
+ if (win == self->tltresize) return OB_FRAME_CONTEXT_TLCORNER;
+ if (win == self->tllresize) return OB_FRAME_CONTEXT_TLCORNER;
+ if (win == self->trtresize) return OB_FRAME_CONTEXT_TRCORNER;
+ if (win == self->trrresize) return OB_FRAME_CONTEXT_TRCORNER;
+ if (win == self->left) return OB_FRAME_CONTEXT_LEFT;
+ if (win == self->right) return OB_FRAME_CONTEXT_RIGHT;
+ if (win == self->innertop) return OB_FRAME_CONTEXT_TITLEBAR;
+ if (win == self->innerleft) return OB_FRAME_CONTEXT_LEFT;
+ if (win == self->innerbottom) return OB_FRAME_CONTEXT_BOTTOM;
+ if (win == self->innerright) return OB_FRAME_CONTEXT_RIGHT;
+ if (win == self->innerbll) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->innerblb) return OB_FRAME_CONTEXT_BLCORNER;
+ if (win == self->innerbrr) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->innerbrb) return OB_FRAME_CONTEXT_BRCORNER;
+ if (win == self->max) return OB_FRAME_CONTEXT_MAXIMIZE;
+ if (win == self->iconify) return OB_FRAME_CONTEXT_ICONIFY;
+ if (win == self->close) return OB_FRAME_CONTEXT_CLOSE;
+ if (win == self->icon) return OB_FRAME_CONTEXT_ICON;
+ if (win == self->desk) return OB_FRAME_CONTEXT_ALLDESKTOPS;
+ if (win == self->shade) return OB_FRAME_CONTEXT_SHADE;
+
+ return OB_FRAME_CONTEXT_NONE;
+}
+
+void frame_client_gravity(ObFrame *self, gint *x, gint *y)
+{
+ /* horizontal */
+ switch (self->client->gravity) {
+ default:
+ case NorthWestGravity:
+ case SouthWestGravity:
+ case WestGravity:
+ break;
+
+ case NorthGravity:
+ case SouthGravity:
+ case CenterGravity:
+ /* the middle of the client will be the middle of the frame */
+ *x -= (self->size.right - self->size.left) / 2;
+ break;
+
+ case NorthEastGravity:
+ case SouthEastGravity:
+ case EastGravity:
+ /* the right side of the client will be the right side of the frame */
+ *x -= self->size.right + self->size.left -
+ self->client->border_width * 2;
+ break;
+
+ case ForgetGravity:
+ case StaticGravity:
+ /* the client's position won't move */
+ *x -= self->size.left - self->client->border_width;
+ break;
+ }
+
+ /* vertical */
+ switch (self->client->gravity) {
+ default:
+ case NorthWestGravity:
+ case NorthEastGravity:
+ case NorthGravity:
+ break;
+
+ case CenterGravity:
+ case EastGravity:
+ case WestGravity:
+ /* the middle of the client will be the middle of the frame */
+ *y -= (self->size.bottom - self->size.top) / 2;
+ break;
+
+ case SouthWestGravity:
+ case SouthEastGravity:
+ case SouthGravity:
+ /* the bottom of the client will be the bottom of the frame */
+ *y -= self->size.bottom + self->size.top -
+ self->client->border_width * 2;
+ break;
+
+ case ForgetGravity:
+ case StaticGravity:
+ /* the client's position won't move */
+ *y -= self->size.top - self->client->border_width;
+ break;
+ }
+}
+
+void frame_frame_gravity(ObFrame *self, gint *x, gint *y)
+{
+ /* horizontal */
+ switch (self->client->gravity) {
+ default:
+ case NorthWestGravity:
+ case WestGravity:
+ case SouthWestGravity:
+ break;
+ case NorthGravity:
+ case CenterGravity:
+ case SouthGravity:
+ /* the middle of the client will be the middle of the frame */
+ *x += (self->size.right - self->size.left) / 2;
+ break;
+ case NorthEastGravity:
+ case EastGravity:
+ case SouthEastGravity:
+ /* the right side of the client will be the right side of the frame */
+ *x += self->size.right + self->size.left -
+ self->client->border_width * 2;
+ break;
+ case StaticGravity:
+ case ForgetGravity:
+ /* the client's position won't move */
+ *x += self->size.left - self->client->border_width;
+ break;
+ }
+
+ /* vertical */
+ switch (self->client->gravity) {
+ default:
+ case NorthWestGravity:
+ case NorthGravity:
+ case NorthEastGravity:
+ break;
+ case WestGravity:
+ case CenterGravity:
+ case EastGravity:
+ /* the middle of the client will be the middle of the frame */
+ *y += (self->size.bottom - self->size.top) / 2;
+ break;
+ case SouthWestGravity:
+ case SouthGravity:
+ case SouthEastGravity:
+ /* the bottom of the client will be the bottom of the frame */
+ *y += self->size.bottom + self->size.top -
+ self->client->border_width * 2;
+ break;
+ case StaticGravity:
+ case ForgetGravity:
+ /* the client's position won't move */
+ *y += self->size.top - self->client->border_width;
+ break;
+ }
+}
+
+void frame_rect_to_frame(ObFrame *self, Rect *r)
+{
+ r->width += self->size.left + self->size.right;
+ r->height += self->size.top + self->size.bottom;
+ frame_client_gravity(self, &r->x, &r->y);
+}
+
+void frame_rect_to_client(ObFrame *self, Rect *r)
+{
+ r->width -= self->size.left + self->size.right;
+ r->height -= self->size.top + self->size.bottom;
+ frame_frame_gravity(self, &r->x, &r->y);
+}
+
+static void flash_done(gpointer data)
+{
+ ObFrame *self = data;
+
+ if (self->focused != self->flash_on)
+ frame_adjust_focus(self, self->focused);
+}
+
+static gboolean flash_timeout(gpointer data)
+{
+ ObFrame *self = data;
+ GTimeVal now;
+
+ g_get_current_time(&now);
+ if (now.tv_sec > self->flash_end.tv_sec ||
+ (now.tv_sec == self->flash_end.tv_sec &&
+ now.tv_usec >= self->flash_end.tv_usec))
+ self->flashing = FALSE;
+
+ if (!self->flashing)
+ return FALSE; /* we are done */
+
+ self->flash_on = !self->flash_on;
+ if (!self->focused) {
+ frame_adjust_focus(self, self->flash_on);
+ self->focused = FALSE;
+ }
+
+ return TRUE; /* go again */
+}
+
+void frame_flash_start(ObFrame *self)
+{
+ self->flash_on = self->focused;
+
+ if (!self->flashing)
+ self->flash_timer = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ 600, flash_timeout, self,
+ flash_done);
+ g_get_current_time(&self->flash_end);
+ g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
+
+ self->flashing = TRUE;
+}
+
+void frame_flash_stop(ObFrame *self)
+{
+ self->flashing = FALSE;
+}
+
+static gulong frame_animate_iconify_time_left(ObFrame *self,
+ const GTimeVal *now)
+{
+ glong sec, usec;
+ sec = self->iconify_animation_end.tv_sec - now->tv_sec;
+ usec = self->iconify_animation_end.tv_usec - now->tv_usec;
+ if (usec < 0) {
+ usec += G_USEC_PER_SEC;
+ sec--;
+ }
+ /* no negative values */
+ return MAX(sec * G_USEC_PER_SEC + usec, 0);
+}
+
+static gboolean frame_animate_iconify(gpointer p)
+{
+ ObFrame *self = p;
+ gint x, y, w, h;
+ gint iconx, icony, iconw;
+ GTimeVal now;
+ gulong time;
+ gboolean iconifying;
+
+ if (self->client->icon_geometry.width == 0) {
+ /* there is no icon geometry set so just go straight down */
+ const Rect *a;
+
+ a = screen_physical_area_monitor(screen_find_monitor(&self->area));
+ iconx = self->area.x + self->area.width / 2 + 32;
+ icony = a->y + a->width;
+ iconw = 64;
+ } else {
+ iconx = self->client->icon_geometry.x;
+ icony = self->client->icon_geometry.y;
+ iconw = self->client->icon_geometry.width;
+ }
+
+ iconifying = self->iconify_animation_going > 0;
+
+ /* how far do we have left to go ? */
+ g_get_current_time(&now);
+ time = frame_animate_iconify_time_left(self, &now);
+
+ if ((time > 0 && iconifying) || (time == 0 && !iconifying)) {
+ /* start where the frame is supposed to be */
+ x = self->area.x;
+ y = self->area.y;
+ w = self->area.width;
+ h = self->area.height;
+ } else {
+ /* start at the icon */
+ x = iconx;
+ y = icony;
+ w = iconw;
+ h = self->size.top; /* just the titlebar */
+ }
+
+ if (time > 0) {
+ glong dx, dy, dw;
+ glong elapsed;
+
+ dx = self->area.x - iconx;
+ dy = self->area.y - icony;
+ dw = self->area.width - self->bwidth * 2 - iconw;
+ /* if restoring, we move in the opposite direction */
+ if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
+
+ elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
+ x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
+ y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
+ w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
+ h = self->size.top; /* just the titlebar */
+ }
+
+ XMoveResizeWindow(obt_display, self->window, x, y, w, h);
+ XFlush(obt_display);
+
+ if (time == 0)
+ frame_end_iconify_animation(self);
+
+ return time > 0; /* repeat until we're out of time */
+}
+
+void frame_end_iconify_animation(ObFrame *self)
+{
+ /* see if there is an animation going */
+ if (self->iconify_animation_going == 0) return;
+
+ if (!self->visible)
+ XUnmapWindow(obt_display, self->window);
+ else {
+ /* Send a ConfigureNotify when the animation is done, this fixes
+ KDE's pager showing the window in the wrong place. since the
+ window is mapped at a different location and is then moved, we
+ need to send the synthetic configurenotify, since apps may have
+ read the position when the client mapped, apparently. */
+ client_reconfigure(self->client, TRUE);
+ }
+
+ /* we're not animating any more ! */
+ self->iconify_animation_going = 0;
+
+ XMoveResizeWindow(obt_display, self->window,
+ self->area.x, self->area.y,
+ self->area.width, self->area.height);
+ /* we delay re-rendering until after we're done animating */
+ framerender_frame(self);
+ XFlush(obt_display);
+}
+
+void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
+{
+ gulong time;
+ gboolean new_anim = FALSE;
+ gboolean set_end = TRUE;
+ GTimeVal now;
+
+ /* if there is no titlebar, just don't animate for now
+ XXX it would be nice tho.. */
+ if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
+ return;
+
+ /* get the current time */
+ g_get_current_time(&now);
+
+ /* get how long until the end */
+ time = FRAME_ANIMATE_ICONIFY_TIME;
+ if (self->iconify_animation_going) {
+ if (!!iconifying != (self->iconify_animation_going > 0)) {
+ /* animation was already going on in the opposite direction */
+ time = time - frame_animate_iconify_time_left(self, &now);
+ } else
+ /* animation was already going in the same direction */
+ set_end = FALSE;
+ } else
+ new_anim = TRUE;
+ self->iconify_animation_going = iconifying ? 1 : -1;
+
+ /* set the ending time */
+ if (set_end) {
+ self->iconify_animation_end.tv_sec = now.tv_sec;
+ self->iconify_animation_end.tv_usec = now.tv_usec;
+ g_time_val_add(&self->iconify_animation_end, time);
+ }
+
+ if (new_anim) {
+ if (self->iconify_animation_timer)
+ g_source_remove(self->iconify_animation_timer);
+ self->iconify_animation_timer =
+ g_timeout_add_full(G_PRIORITY_DEFAULT,
+ FRAME_ANIMATE_ICONIFY_STEP_TIME,
+ frame_animate_iconify, self, NULL);
+
+
+ /* do the first step */
+ frame_animate_iconify(self);
+
+ /* show it during the animation even if it is not "visible" */
+ if (!self->visible)
+ XMapWindow(obt_display, self->window);
+ }
+}
diff --git a/openbox/frame.h b/openbox/frame.h
new file mode 100644
index 0000000..915c08d
--- /dev/null
+++ b/openbox/frame.h
@@ -0,0 +1,272 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ frame.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __frame_h
+#define __frame_h
+
+#include "geom.h"
+#include "obrender/render.h"
+
+typedef struct _ObFrame ObFrame;
+
+struct _ObClient;
+
+typedef void (*ObFrameIconifyAnimateFunc)(gpointer data);
+
+typedef enum {
+ OB_FRAME_CONTEXT_NONE,
+ OB_FRAME_CONTEXT_DESKTOP,
+ OB_FRAME_CONTEXT_ROOT,
+ OB_FRAME_CONTEXT_CLIENT,
+ OB_FRAME_CONTEXT_TITLEBAR,
+ OB_FRAME_CONTEXT_FRAME,
+ OB_FRAME_CONTEXT_BLCORNER,
+ OB_FRAME_CONTEXT_BRCORNER,
+ OB_FRAME_CONTEXT_TLCORNER,
+ OB_FRAME_CONTEXT_TRCORNER,
+ OB_FRAME_CONTEXT_TOP,
+ OB_FRAME_CONTEXT_BOTTOM,
+ OB_FRAME_CONTEXT_LEFT,
+ OB_FRAME_CONTEXT_RIGHT,
+ OB_FRAME_CONTEXT_MAXIMIZE,
+ OB_FRAME_CONTEXT_ALLDESKTOPS,
+ OB_FRAME_CONTEXT_SHADE,
+ OB_FRAME_CONTEXT_ICONIFY,
+ OB_FRAME_CONTEXT_ICON,
+ OB_FRAME_CONTEXT_CLOSE,
+ /*! This is a special context, which occurs while dragging a window in
+ a move/resize */
+ OB_FRAME_CONTEXT_MOVE_RESIZE,
+ OB_FRAME_CONTEXT_DOCK,
+ OB_FRAME_NUM_CONTEXTS
+} ObFrameContext;
+
+#define FRAME_CONTEXT(co, cl) ((cl && cl->type != OB_CLIENT_TYPE_DESKTOP) ? \
+ co == OB_FRAME_CONTEXT_FRAME : FALSE)
+#define CLIENT_CONTEXT(co, cl) ((cl && cl->type == OB_CLIENT_TYPE_DESKTOP) ? \
+ co == OB_FRAME_CONTEXT_DESKTOP : \
+ co == OB_FRAME_CONTEXT_CLIENT)
+
+/*! The decorations the client window wants to be displayed on it */
+typedef enum {
+ OB_FRAME_DECOR_TITLEBAR = 1 << 0, /*!< Display a titlebar */
+ OB_FRAME_DECOR_HANDLE = 1 << 1, /*!< Display a handle (bottom) */
+ OB_FRAME_DECOR_GRIPS = 1 << 2, /*!< Display grips in the handle */
+ OB_FRAME_DECOR_BORDER = 1 << 3, /*!< Display a border */
+ OB_FRAME_DECOR_ICON = 1 << 4, /*!< Display the window's icon */
+ OB_FRAME_DECOR_ICONIFY = 1 << 5, /*!< Display an iconify button */
+ OB_FRAME_DECOR_MAXIMIZE = 1 << 6, /*!< Display a maximize button */
+ /*! Display a button to toggle the window's placement on
+ all desktops */
+ OB_FRAME_DECOR_ALLDESKTOPS = 1 << 7,
+ OB_FRAME_DECOR_SHADE = 1 << 8, /*!< Display a shade button */
+ OB_FRAME_DECOR_CLOSE = 1 << 9 /*!< Display a close button */
+} ObFrameDecorations;
+
+struct _ObFrame
+{
+ struct _ObClient *client;
+
+ Window window;
+
+ Strut size; /* the size of the frame */
+ Strut oldsize; /* the size of the frame last told to the client */
+ Rect area;
+ gboolean visible;
+
+ guint functions;
+ guint decorations;
+
+ Window title;
+ Window label;
+ Window max;
+ Window close;
+ Window desk;
+ Window shade;
+ Window icon;
+ Window iconify;
+ Window handle;
+ Window lgrip;
+ Window rgrip;
+
+ /* These are borders of the frame and its elements */
+ Window titleleft;
+ Window titletop;
+ Window titletopleft;
+ Window titletopright;
+ Window titleright;
+ Window titlebottom;
+ Window left;
+ Window right;
+ Window handleleft;
+ Window handletop;
+ Window handleright;
+ Window handlebottom;
+ Window lgriptop;
+ Window lgripleft;
+ Window lgripbottom;
+ Window rgriptop;
+ Window rgripright;
+ Window rgripbottom;
+ Window innerleft; /*!< For drawing the inner client border */
+ Window innertop; /*!< For drawing the inner client border */
+ Window innerright; /*!< For drawing the inner client border */
+ Window innerbottom; /*!< For drawing the inner client border */
+ Window innerblb;
+ Window innerbll;
+ Window innerbrb;
+ Window innerbrr;
+ Window backback; /*!< A colored window shown while resizing */
+ Window backfront; /*!< An undrawn-in window, to prevent flashing on
+ unmap */
+
+ /* These are resize handles inside the titlebar */
+ Window topresize;
+ Window tltresize;
+ Window tllresize;
+ Window trtresize;
+ Window trrresize;
+
+ Colormap colormap;
+
+ gint icon_on; /* if the window icon button is on */
+ gint label_on; /* if the window title is on */
+ gint iconify_on; /* if the window iconify button is on */
+ gint desk_on; /* if the window all-desktops button is on */
+ gint shade_on; /* if the window shade button is on */
+ gint max_on; /* if the window maximize button is on */
+ gint close_on; /* if the window close button is on */
+
+ gint width; /* width of the titlebar and handle */
+ gint label_width; /* width of the label in the titlebar */
+ gint icon_x; /* x-position of the window icon button */
+ gint label_x; /* x-position of the window title */
+ gint iconify_x; /* x-position of the window iconify button */
+ gint desk_x; /* x-position of the window all-desktops button */
+ gint shade_x; /* x-position of the window shade button */
+ gint max_x; /* x-position of the window maximize button */
+ gint close_x; /* x-position of the window close button */
+ gint bwidth; /* border width */
+ gint cbwidth_l; /* client border width */
+ gint cbwidth_t; /* client border width */
+ gint cbwidth_r; /* client border width */
+ gint cbwidth_b; /* client border width */
+ gboolean max_horz; /* when maxed some decorations are hidden */
+ gboolean max_vert; /* when maxed some decorations are hidden */
+ gboolean shaded; /* decorations adjust when shaded */
+
+ /* the leftmost and rightmost elements in the titlebar */
+ ObFrameContext leftmost;
+ ObFrameContext rightmost;
+
+ gboolean max_press;
+ gboolean close_press;
+ gboolean desk_press;
+ gboolean shade_press;
+ gboolean iconify_press;
+ gboolean max_hover;
+ gboolean close_hover;
+ gboolean desk_hover;
+ gboolean shade_hover;
+ gboolean iconify_hover;
+
+ gboolean focused;
+ gboolean need_render;
+
+ gboolean flashing;
+ gboolean flash_on;
+ GTimeVal flash_end;
+ guint flash_timer;
+
+ /*! Is the frame currently in an animation for iconify or restore.
+ 0 means that it is not animating. > 0 means it is animating an iconify.
+ < 0 means it is animating a restore.
+ */
+ gint iconify_animation_going;
+ guint iconify_animation_timer;
+ GTimeVal iconify_animation_end;
+};
+
+ObFrame *frame_new(struct _ObClient *c);
+void frame_free(ObFrame *self);
+
+void frame_show(ObFrame *self);
+void frame_hide(ObFrame *self);
+void frame_adjust_theme(ObFrame *self);
+#ifdef SHAPE
+void frame_adjust_shape_kind(ObFrame *self, int kind);
+#endif
+void frame_adjust_shape(ObFrame *self);
+void frame_adjust_area(ObFrame *self, gboolean moved,
+ gboolean resized, gboolean fake);
+void frame_adjust_client_area(ObFrame *self);
+void frame_adjust_state(ObFrame *self);
+void frame_adjust_focus(ObFrame *self, gboolean hilite);
+void frame_adjust_title(ObFrame *self);
+void frame_adjust_icon(ObFrame *self);
+void frame_grab_client(ObFrame *self);
+void frame_release_client(ObFrame *self);
+
+ObFrameContext frame_context_from_string(const gchar *name);
+
+/*! Parses a ObFrameContext from a string of space-separated context names.
+ @names The list of context names, the first of which is removed from the
+ string.
+ @cx The ObFrameContext is returned here. If an invalid name is found, this
+ is set to OB_FRAME_CONTEXT_NONE.
+ @return TRUE if there was something to read in @names, FALSE if it was an
+ empty input.
+*/
+gboolean frame_next_context_from_string(gchar *names, ObFrameContext *cx);
+
+ObFrameContext frame_context(struct _ObClient *self, Window win,
+ gint x, gint y);
+
+/*! Applies gravity to the client's position to find where the frame should
+ be positioned.
+ @return The proper coordinates for the frame, based on the client.
+*/
+void frame_client_gravity(ObFrame *self, gint *x, gint *y);
+
+/*! Reversly applies gravity to the frame's position to find where the client
+ should be positioned.
+ @return The proper coordinates for the client, based on the frame.
+*/
+void frame_frame_gravity(ObFrame *self, gint *x, gint *y);
+
+/*! Convert a rectangle in client coordinates/sizes to what it would be
+ for the frame, given its current decorations sizes */
+void frame_rect_to_frame(ObFrame *self, Rect *r);
+
+/*! Convert a rectangle in frame coordinates/sizes to what it would be for the
+ client, given its current decorations sizes */
+void frame_rect_to_client(ObFrame *self, Rect *r);
+
+void frame_flash_start(ObFrame *self);
+void frame_flash_stop(ObFrame *self);
+
+/*! Start an animation for iconifying or restoring a frame. The callback
+ will be called when the animation finishes. But if another animation is
+ started in the meantime, the callback will never get called. */
+void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying);
+void frame_end_iconify_animation(ObFrame *self);
+
+#define frame_iconify_animating(f) (f->iconify_animation_going != 0)
+
+#endif
diff --git a/openbox/framerender.c b/openbox/framerender.c
new file mode 100644
index 0000000..041e6d1
--- /dev/null
+++ b/openbox/framerender.c
@@ -0,0 +1,412 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ framerender.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "frame.h"
+#include "openbox.h"
+#include "screen.h"
+#include "client.h"
+#include "framerender.h"
+#include "obrender/theme.h"
+
+static void framerender_label(ObFrame *self, RrAppearance *a);
+static void framerender_icon(ObFrame *self, RrAppearance *a);
+static void framerender_max(ObFrame *self, RrAppearance *a);
+static void framerender_iconify(ObFrame *self, RrAppearance *a);
+static void framerender_desk(ObFrame *self, RrAppearance *a);
+static void framerender_shade(ObFrame *self, RrAppearance *a);
+static void framerender_close(ObFrame *self, RrAppearance *a);
+
+void framerender_frame(ObFrame *self)
+{
+ if (frame_iconify_animating(self))
+ return; /* delay redrawing until the animation is done */
+ if (!self->need_render)
+ return;
+ if (!self->visible)
+ return;
+ self->need_render = FALSE;
+
+ {
+ gulong px;
+
+ px = (self->focused ?
+ RrColorPixel(ob_rr_theme->cb_focused_color) :
+ RrColorPixel(ob_rr_theme->cb_unfocused_color));
+
+ XSetWindowBackground(obt_display, self->backback, px);
+ XClearWindow(obt_display, self->backback);
+ XSetWindowBackground(obt_display, self->innerleft, px);
+ XClearWindow(obt_display, self->innerleft);
+ XSetWindowBackground(obt_display, self->innertop, px);
+ XClearWindow(obt_display, self->innertop);
+ XSetWindowBackground(obt_display, self->innerright, px);
+ XClearWindow(obt_display, self->innerright);
+ XSetWindowBackground(obt_display, self->innerbottom, px);
+ XClearWindow(obt_display, self->innerbottom);
+ XSetWindowBackground(obt_display, self->innerbll, px);
+ XClearWindow(obt_display, self->innerbll);
+ XSetWindowBackground(obt_display, self->innerbrr, px);
+ XClearWindow(obt_display, self->innerbrr);
+ XSetWindowBackground(obt_display, self->innerblb, px);
+ XClearWindow(obt_display, self->innerblb);
+ XSetWindowBackground(obt_display, self->innerbrb, px);
+ XClearWindow(obt_display, self->innerbrb);
+
+ px = RrColorPixel(self->focused ?
+ (self->client->undecorated ?
+ ob_rr_theme->frame_undecorated_focused_border_color :
+ ob_rr_theme->frame_focused_border_color) :
+ (self->client->undecorated ?
+ ob_rr_theme->frame_undecorated_unfocused_border_color :
+ ob_rr_theme->frame_unfocused_border_color));
+
+ XSetWindowBackground(obt_display, self->left, px);
+ XClearWindow(obt_display, self->left);
+ XSetWindowBackground(obt_display, self->right, px);
+ XClearWindow(obt_display, self->right);
+
+ XSetWindowBackground(obt_display, self->titleleft, px);
+ XClearWindow(obt_display, self->titleleft);
+ XSetWindowBackground(obt_display, self->titletop, px);
+ XClearWindow(obt_display, self->titletop);
+ XSetWindowBackground(obt_display, self->titletopleft, px);
+ XClearWindow(obt_display, self->titletopleft);
+ XSetWindowBackground(obt_display, self->titletopright, px);
+ XClearWindow(obt_display, self->titletopright);
+ XSetWindowBackground(obt_display, self->titleright, px);
+ XClearWindow(obt_display, self->titleright);
+
+ XSetWindowBackground(obt_display, self->handleleft, px);
+ XClearWindow(obt_display, self->handleleft);
+ XSetWindowBackground(obt_display, self->handletop, px);
+ XClearWindow(obt_display, self->handletop);
+ XSetWindowBackground(obt_display, self->handleright, px);
+ XClearWindow(obt_display, self->handleright);
+ XSetWindowBackground(obt_display, self->handlebottom, px);
+ XClearWindow(obt_display, self->handlebottom);
+
+ XSetWindowBackground(obt_display, self->lgripleft, px);
+ XClearWindow(obt_display, self->lgripleft);
+ XSetWindowBackground(obt_display, self->lgriptop, px);
+ XClearWindow(obt_display, self->lgriptop);
+ XSetWindowBackground(obt_display, self->lgripbottom, px);
+ XClearWindow(obt_display, self->lgripbottom);
+
+ XSetWindowBackground(obt_display, self->rgripright, px);
+ XClearWindow(obt_display, self->rgripright);
+ XSetWindowBackground(obt_display, self->rgriptop, px);
+ XClearWindow(obt_display, self->rgriptop);
+ XSetWindowBackground(obt_display, self->rgripbottom, px);
+ XClearWindow(obt_display, self->rgripbottom);
+
+ /* don't use the separator color for shaded windows */
+ if (!self->client->shaded)
+ px = (self->focused ?
+ RrColorPixel(ob_rr_theme->title_separator_focused_color) :
+ RrColorPixel(ob_rr_theme->title_separator_unfocused_color));
+
+ XSetWindowBackground(obt_display, self->titlebottom, px);
+ XClearWindow(obt_display, self->titlebottom);
+ }
+
+ if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
+ RrAppearance *t, *l, *m, *n, *i, *d, *s, *c, *clear;
+ if (self->focused) {
+ t = ob_rr_theme->a_focused_title;
+ l = ob_rr_theme->a_focused_label;
+ m = (!(self->decorations & OB_FRAME_DECOR_MAXIMIZE) ?
+ ob_rr_theme->btn_max->a_disabled_focused :
+ (self->client->max_vert || self->client->max_horz ?
+ (self->max_press ?
+ ob_rr_theme->btn_max->a_toggled_focused_pressed :
+ (self->max_hover ?
+ ob_rr_theme->btn_max->a_toggled_hover_focused :
+ ob_rr_theme->btn_max->a_toggled_focused_unpressed)) :
+ (self->max_press ?
+ ob_rr_theme->btn_max->a_focused_pressed :
+ (self->max_hover ?
+ ob_rr_theme->btn_max->a_hover_focused :
+ ob_rr_theme->btn_max->a_focused_unpressed))));
+ n = ob_rr_theme->a_icon;
+ i = (!(self->decorations & OB_FRAME_DECOR_ICONIFY) ?
+ ob_rr_theme->btn_iconify->a_disabled_focused :
+ (self->iconify_press ?
+ ob_rr_theme->btn_iconify->a_focused_pressed :
+ (self->iconify_hover ?
+ ob_rr_theme->btn_iconify->a_hover_focused :
+ ob_rr_theme->btn_iconify->a_focused_unpressed)));
+ d = (!(self->decorations & OB_FRAME_DECOR_ALLDESKTOPS) ?
+ ob_rr_theme->btn_desk->a_disabled_focused :
+ (self->client->desktop == DESKTOP_ALL ?
+ (self->desk_press ?
+ ob_rr_theme->btn_desk->a_toggled_focused_pressed :
+ (self->desk_hover ?
+ ob_rr_theme->btn_desk->a_toggled_hover_focused :
+ ob_rr_theme->btn_desk->a_toggled_focused_unpressed)) :
+ (self->desk_press ?
+ ob_rr_theme->btn_desk->a_focused_pressed :
+ (self->desk_hover ?
+ ob_rr_theme->btn_desk->a_hover_focused :
+ ob_rr_theme->btn_desk->a_focused_unpressed))));
+ s = (!(self->decorations & OB_FRAME_DECOR_SHADE) ?
+ ob_rr_theme->btn_shade->a_disabled_focused :
+ (self->client->shaded ?
+ (self->shade_press ?
+ ob_rr_theme->btn_shade->a_toggled_focused_pressed :
+ (self->shade_hover ?
+ ob_rr_theme->btn_shade->a_toggled_hover_focused :
+ ob_rr_theme->btn_shade->a_toggled_focused_unpressed)) :
+ (self->shade_press ?
+ ob_rr_theme->btn_shade->a_focused_pressed :
+ (self->shade_hover ?
+ ob_rr_theme->btn_shade->a_hover_focused :
+ ob_rr_theme->btn_shade->a_focused_unpressed))));
+ c = (!(self->decorations & OB_FRAME_DECOR_CLOSE) ?
+ ob_rr_theme->btn_close->a_disabled_focused :
+ (self->close_press ?
+ ob_rr_theme->btn_close->a_focused_pressed :
+ (self->close_hover ?
+ ob_rr_theme->btn_close->a_hover_focused :
+ ob_rr_theme->btn_close->a_focused_unpressed)));
+ } else {
+ t = ob_rr_theme->a_unfocused_title;
+ l = ob_rr_theme->a_unfocused_label;
+ m = (!(self->decorations & OB_FRAME_DECOR_MAXIMIZE) ?
+ ob_rr_theme->btn_max->a_disabled_unfocused :
+ (self->client->max_vert || self->client->max_horz ?
+ (self->max_press ?
+ ob_rr_theme->btn_max->a_toggled_unfocused_pressed :
+ (self->max_hover ?
+ ob_rr_theme->btn_max->a_toggled_hover_unfocused :
+ ob_rr_theme->btn_max->a_toggled_unfocused_unpressed)) :
+ (self->max_press ?
+ ob_rr_theme->btn_max->a_unfocused_pressed :
+ (self->max_hover ?
+ ob_rr_theme->btn_max->a_hover_unfocused :
+ ob_rr_theme->btn_max->a_unfocused_unpressed))));
+ n = ob_rr_theme->a_icon;
+ i = (!(self->decorations & OB_FRAME_DECOR_ICONIFY) ?
+ ob_rr_theme->btn_iconify->a_disabled_unfocused :
+ (self->iconify_press ?
+ ob_rr_theme->btn_iconify->a_unfocused_pressed :
+ (self->iconify_hover ?
+ ob_rr_theme->btn_iconify->a_hover_unfocused :
+ ob_rr_theme->btn_iconify->a_unfocused_unpressed)));
+ d = (!(self->decorations & OB_FRAME_DECOR_ALLDESKTOPS) ?
+ ob_rr_theme->btn_desk->a_disabled_unfocused :
+ (self->client->desktop == DESKTOP_ALL ?
+ (self->desk_press ?
+ ob_rr_theme->btn_desk->a_toggled_unfocused_pressed :
+ (self->desk_hover ?
+ ob_rr_theme->btn_desk->a_toggled_hover_unfocused :
+ ob_rr_theme->btn_desk->a_toggled_unfocused_unpressed)) :
+ (self->desk_press ?
+ ob_rr_theme->btn_desk->a_unfocused_pressed :
+ (self->desk_hover ?
+ ob_rr_theme->btn_desk->a_hover_unfocused :
+ ob_rr_theme->btn_desk->a_unfocused_unpressed))));
+ s = (!(self->decorations & OB_FRAME_DECOR_SHADE) ?
+ ob_rr_theme->btn_shade->a_disabled_unfocused :
+ (self->client->shaded ?
+ (self->shade_press ?
+ ob_rr_theme->btn_shade->a_toggled_unfocused_pressed :
+ (self->shade_hover ?
+ ob_rr_theme->btn_shade->a_toggled_hover_unfocused :
+ ob_rr_theme->btn_shade->a_toggled_unfocused_unpressed)) :
+ (self->shade_press ?
+ ob_rr_theme->btn_shade->a_unfocused_pressed :
+ (self->shade_hover ?
+ ob_rr_theme->btn_shade->a_hover_unfocused :
+ ob_rr_theme->btn_shade->a_unfocused_unpressed))));
+ c = (!(self->decorations & OB_FRAME_DECOR_CLOSE) ?
+ ob_rr_theme->btn_close->a_disabled_unfocused :
+ (self->close_press ?
+ ob_rr_theme->btn_close->a_unfocused_pressed :
+ (self->close_hover ?
+ ob_rr_theme->btn_close->a_hover_unfocused :
+ ob_rr_theme->btn_close->a_unfocused_unpressed)));
+ }
+ clear = ob_rr_theme->a_clear;
+
+ RrPaint(t, self->title, self->width, ob_rr_theme->title_height);
+
+ clear->surface.parent = t;
+ clear->surface.parenty = 0;
+
+ clear->surface.parentx = ob_rr_theme->grip_width;
+
+ RrPaint(clear, self->topresize,
+ self->width - ob_rr_theme->grip_width * 2,
+ ob_rr_theme->paddingy + 1);
+
+ clear->surface.parentx = 0;
+
+ if (ob_rr_theme->grip_width > 0)
+ RrPaint(clear, self->tltresize,
+ ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
+ if (ob_rr_theme->title_height > 0)
+ RrPaint(clear, self->tllresize,
+ ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
+
+ clear->surface.parentx = self->width - ob_rr_theme->grip_width;
+
+ if (ob_rr_theme->grip_width > 0)
+ RrPaint(clear, self->trtresize,
+ ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
+
+ clear->surface.parentx = self->width - (ob_rr_theme->paddingx + 1);
+
+ if (ob_rr_theme->title_height > 0)
+ RrPaint(clear, self->trrresize,
+ ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
+
+ /* set parents for any parent relative guys */
+ l->surface.parent = t;
+ l->surface.parentx = self->label_x;
+ l->surface.parenty = ob_rr_theme->paddingy;
+
+ m->surface.parent = t;
+ m->surface.parentx = self->max_x;
+ m->surface.parenty = ob_rr_theme->paddingy + 1;
+
+ n->surface.parent = t;
+ n->surface.parentx = self->icon_x;
+ n->surface.parenty = ob_rr_theme->paddingy;
+
+ i->surface.parent = t;
+ i->surface.parentx = self->iconify_x;
+ i->surface.parenty = ob_rr_theme->paddingy + 1;
+
+ d->surface.parent = t;
+ d->surface.parentx = self->desk_x;
+ d->surface.parenty = ob_rr_theme->paddingy + 1;
+
+ s->surface.parent = t;
+ s->surface.parentx = self->shade_x;
+ s->surface.parenty = ob_rr_theme->paddingy + 1;
+
+ c->surface.parent = t;
+ c->surface.parentx = self->close_x;
+ c->surface.parenty = ob_rr_theme->paddingy + 1;
+
+ framerender_label(self, l);
+ framerender_max(self, m);
+ framerender_icon(self, n);
+ framerender_iconify(self, i);
+ framerender_desk(self, d);
+ framerender_shade(self, s);
+ framerender_close(self, c);
+ }
+
+ if (self->decorations & OB_FRAME_DECOR_HANDLE &&
+ ob_rr_theme->handle_height > 0)
+ {
+ RrAppearance *h, *g;
+
+ h = (self->focused ?
+ ob_rr_theme->a_focused_handle : ob_rr_theme->a_unfocused_handle);
+
+ RrPaint(h, self->handle, self->width, ob_rr_theme->handle_height);
+
+ if (self->decorations & OB_FRAME_DECOR_GRIPS) {
+ g = (self->focused ?
+ ob_rr_theme->a_focused_grip : ob_rr_theme->a_unfocused_grip);
+
+ if (g->surface.grad == RR_SURFACE_PARENTREL)
+ g->surface.parent = h;
+
+ g->surface.parentx = 0;
+ g->surface.parenty = 0;
+
+ RrPaint(g, self->lgrip,
+ ob_rr_theme->grip_width, ob_rr_theme->handle_height);
+
+ g->surface.parentx = self->width - ob_rr_theme->grip_width;
+ g->surface.parenty = 0;
+
+ RrPaint(g, self->rgrip,
+ ob_rr_theme->grip_width, ob_rr_theme->handle_height);
+ }
+ }
+
+ XFlush(obt_display);
+}
+
+static void framerender_label(ObFrame *self, RrAppearance *a)
+{
+ if (!self->label_on) return;
+ /* set the texture's text! */
+ a->texture[0].data.text.string = self->client->title;
+ RrPaint(a, self->label, self->label_width, ob_rr_theme->label_height);
+}
+
+static void framerender_icon(ObFrame *self, RrAppearance *a)
+{
+ RrImage *icon;
+
+ if (!self->icon_on) return;
+
+ icon = client_icon(self->client);
+
+ if (icon) {
+ RrAppearanceClearTextures(a);
+ a->texture[0].type = RR_TEXTURE_IMAGE;
+ a->texture[0].data.image.alpha = 0xff;
+ a->texture[0].data.image.image = icon;
+ } else {
+ RrAppearanceClearTextures(a);
+ a->texture[0].type = RR_TEXTURE_NONE;
+ }
+
+ RrPaint(a, self->icon,
+ ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
+}
+
+static void framerender_max(ObFrame *self, RrAppearance *a)
+{
+ if (!self->max_on) return;
+ RrPaint(a, self->max, ob_rr_theme->button_size, ob_rr_theme->button_size);
+}
+
+static void framerender_iconify(ObFrame *self, RrAppearance *a)
+{
+ if (!self->iconify_on) return;
+ RrPaint(a, self->iconify,
+ ob_rr_theme->button_size, ob_rr_theme->button_size);
+}
+
+static void framerender_desk(ObFrame *self, RrAppearance *a)
+{
+ if (!self->desk_on) return;
+ RrPaint(a, self->desk, ob_rr_theme->button_size, ob_rr_theme->button_size);
+}
+
+static void framerender_shade(ObFrame *self, RrAppearance *a)
+{
+ if (!self->shade_on) return;
+ RrPaint(a, self->shade,
+ ob_rr_theme->button_size, ob_rr_theme->button_size);
+}
+
+static void framerender_close(ObFrame *self, RrAppearance *a)
+{
+ if (!self->close_on) return;
+ RrPaint(a, self->close,
+ ob_rr_theme->button_size, ob_rr_theme->button_size);
+}
diff --git a/openbox/framerender.h b/openbox/framerender.h
new file mode 100644
index 0000000..162fa57
--- /dev/null
+++ b/openbox/framerender.h
@@ -0,0 +1,26 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ framerender.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __framerender_h
+#define __framerender_h
+
+struct _ObFrame;
+
+void framerender_frame(struct _ObFrame *self);
+
+#endif
diff --git a/openbox/geom.h b/openbox/geom.h
new file mode 100644
index 0000000..003b008
--- /dev/null
+++ b/openbox/geom.h
@@ -0,0 +1,166 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ geom.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __geom_h
+#define __geom_h
+
+#include <glib.h>
+
+typedef struct _GravityCoord {
+ gint pos;
+ gint denom;
+ gboolean center;
+ gboolean opposite;
+} GravityCoord;
+
+typedef struct _GravityPoint {
+ GravityCoord x;
+ GravityCoord y;
+} GravityPoint;
+
+#define GRAVITY_COORD_SET(c, p, cen, opp) \
+ (c).pos = (p), (c).center = (cen), (c).opposite = (opp)
+
+
+typedef struct _Point {
+ int x;
+ int y;
+} Point;
+
+#define POINT_SET(pt, nx, ny) (pt).x = (nx), (pt).y = (ny)
+#define POINT_EQUAL(p1, p2) ((p1).x == (p2).x && (p1).y == (p2).y)
+
+typedef struct _Size {
+ int width;
+ int height;
+} Size;
+
+#define SIZE_SET(sz, w, h) (sz).width = (w), (sz).height = (h)
+
+typedef struct _Rect {
+ int x;
+ int y;
+ int width;
+ int height;
+} Rect;
+
+#define RECT_LEFT(r) ((r).x)
+#define RECT_TOP(r) ((r).y)
+#define RECT_RIGHT(r) ((r).x + (r).width - 1)
+#define RECT_BOTTOM(r) ((r).y + (r).height - 1)
+
+#define RECT_SET_POINT(r, nx, ny) \
+ (r).x = (nx), (r).y = (ny)
+#define RECT_SET_SIZE(r, w, h) \
+ (r).width = (w), (r).height = (h)
+#define RECT_SET(r, nx, ny, w, h) \
+ (r).x = (nx), (r).y = (ny), (r).width = (w), (r).height = (h)
+
+#define RECT_EQUAL(r1, r2) ((r1).x == (r2).x && (r1).y == (r2).y && \
+ (r1).width == (r2).width && \
+ (r1).height == (r2).height)
+#define RECT_EQUAL_DIMS(r, x, y, w, h) \
+ ((r).x == (x) && (r).y == (y) && (r).width == (w) && (r).height == (h))
+
+#define RECT_TO_DIMS(r, x, y, w, h) \
+ (x) = (r).x, (y) = (r).y, (w) = (r).width, (h) = (r).height
+
+#define RECT_CONTAINS(r, px, py) \
+ ((px) >= (r).x && (px) < (r).x + (r).width && \
+ (py) >= (r).y && (py) < (r).y + (r).height)
+#define RECT_CONTAINS_RECT(r, o) \
+ ((o).x >= (r).x && (o).x + (o).width <= (r).x + (r).width && \
+ (o).y >= (r).y && (o).y + (o).height <= (r).y + (r).height)
+
+/* Returns true if Rect r and o intersect */
+#define RECT_INTERSECTS_RECT(r, o) \
+ ((o).x < (r).x + (r).width && (o).x + (o).width > (r).x && \
+ (o).y < (r).y + (r).height && (o).y + (o).height > (r).y)
+
+/* Sets Rect r to be the intersection of Rect a and b. */
+#define RECT_SET_INTERSECTION(r, a, b) \
+ ((r).x = MAX((a).x, (b).x), \
+ (r).y = MAX((a).y, (b).y), \
+ (r).width = MIN((a).x + (a).width - 1, \
+ (b).x + (b).width - 1) - (r).x + 1, \
+ (r).height = MIN((a).y + (a).height - 1, \
+ (b).y + (b).height - 1) - (r).y + 1)
+
+typedef struct _Strut {
+ int left;
+ int top;
+ int right;
+ int bottom;
+} Strut;
+
+typedef struct _StrutPartial {
+ int left;
+ int top;
+ int right;
+ int bottom;
+
+ int left_start, left_end;
+ int top_start, top_end;
+ int right_start, right_end;
+ int bottom_start, bottom_end;
+} StrutPartial;
+
+#define STRUT_SET(s, l, t, r, b) \
+ (s).left = (l), (s).top = (t), (s).right = (r), (s).bottom = (b)
+
+#define STRUT_PARTIAL_SET(s, l, t, r, b, ls, le, ts, te, rs, re, bs, be) \
+ (s).left = (l), (s).top = (t), (s).right = (r), (s).bottom = (b), \
+ (s).left_start = (ls), (s).left_end = (le), \
+ (s).top_start = (ts), (s).top_end = (te), \
+ (s).right_start = (rs), (s).right_end = (re), \
+ (s).bottom_start = (bs), (s).bottom_end = (be)
+
+#define STRUT_ADD(s1, s2) \
+ (s1).left = MAX((s1).left, (s2).left), \
+ (s1).right = MAX((s1).right, (s2).right), \
+ (s1).top = MAX((s1).top, (s2).top), \
+ (s1).bottom = MAX((s1).bottom, (s2).bottom)
+
+#define STRUT_EXISTS(s1) \
+ ((s1).left || (s1).top || (s1).right || (s1).bottom)
+
+#define STRUT_EQUAL(s1, s2) \
+ ((s1).left == (s2).left && \
+ (s1).top == (s2).top && \
+ (s1).right == (s2).right && \
+ (s1).bottom == (s2).bottom)
+
+#define PARTIAL_STRUT_EQUAL(s1, s2) \
+ ((s1).left == (s2).left && \
+ (s1).top == (s2).top && \
+ (s1).right == (s2).right && \
+ (s1).bottom == (s2).bottom && \
+ (s1).left_start == (s2).left_start && \
+ (s1).left_end == (s2).left_end && \
+ (s1).top_start == (s2).top_start && \
+ (s1).top_end == (s2).top_end && \
+ (s1).right_start == (s2).right_start && \
+ (s1).right_end == (s2).right_end && \
+ (s1).bottom_start == (s2).bottom_start && \
+ (s1).bottom_end == (s2).bottom_end)
+
+#define RANGES_INTERSECT(r1x, r1w, r2x, r2w) \
+ (r1w && r2w && r1x < r2x + r2w && r1x + r1w > r2x)
+
+#endif
diff --git a/openbox/grab.c b/openbox/grab.c
new file mode 100644
index 0000000..4518c43
--- /dev/null
+++ b/openbox/grab.c
@@ -0,0 +1,244 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ grab.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "grab.h"
+#include "openbox.h"
+#include "event.h"
+#include "screen.h"
+#include "debug.h"
+#include "obt/display.h"
+#include "obt/keyboard.h"
+
+#include <glib.h>
+#include <X11/Xlib.h>
+
+#define GRAB_PTR_MASK (ButtonPressMask | ButtonReleaseMask | PointerMotionMask)
+#define GRAB_KEY_MASK (KeyPressMask | KeyReleaseMask)
+
+#define MASK_LIST_SIZE 8
+
+/*! A list of all possible combinations of keyboard lock masks */
+static guint mask_list[MASK_LIST_SIZE];
+static guint kgrabs = 0;
+static guint pgrabs = 0;
+/*! The time at which the last grab was made */
+static Time grab_time = CurrentTime;
+static gint passive_count = 0;
+static ObtIC *ic = NULL;
+
+static Time ungrab_time(void)
+{
+ Time t = event_time();
+ if (grab_time == CurrentTime ||
+ !(t == CurrentTime || event_time_after(t, grab_time)))
+ /* When the time moves backward on the server, then we can't use
+ the grab time because that will be in the future. So instead we
+ have to use CurrentTime.
+
+ "XUngrabPointer does not release the pointer if the specified time
+ is earlier than the last-pointer-grab time or is later than the
+ current X server time."
+ */
+ t = CurrentTime; /*grab_time;*/
+ return t;
+}
+
+static Window grab_window(void)
+{
+ return screen_support_win;
+}
+
+gboolean grab_on_keyboard(void)
+{
+ return kgrabs > 0;
+}
+
+gboolean grab_on_pointer(void)
+{
+ return pgrabs > 0;
+}
+
+ObtIC *grab_input_context(void)
+{
+ return ic;
+}
+
+gboolean grab_keyboard_full(gboolean grab)
+{
+ gboolean ret = FALSE;
+
+ if (grab) {
+ if (kgrabs++ == 0) {
+ ret = XGrabKeyboard(obt_display, grab_window(),
+ False, GrabModeAsync, GrabModeAsync,
+ event_time()) == Success;
+ if (!ret)
+ --kgrabs;
+ else {
+ passive_count = 0;
+ grab_time = event_time();
+ }
+ } else
+ ret = TRUE;
+ } else if (kgrabs > 0) {
+ if (--kgrabs == 0) {
+ XUngrabKeyboard(obt_display, ungrab_time());
+ }
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+gboolean grab_pointer_full(gboolean grab, gboolean owner_events,
+ gboolean confine, ObCursor cur)
+{
+ gboolean ret = FALSE;
+
+ if (grab) {
+ if (pgrabs++ == 0) {
+ ret = XGrabPointer(obt_display, grab_window(), owner_events,
+ GRAB_PTR_MASK,
+ GrabModeAsync, GrabModeAsync,
+ (confine ? obt_root(ob_screen) : None),
+ ob_cursor(cur), event_time()) == Success;
+ if (!ret)
+ --pgrabs;
+ else
+ grab_time = event_time();
+ } else
+ ret = TRUE;
+ } else if (pgrabs > 0) {
+ if (--pgrabs == 0) {
+ XUngrabPointer(obt_display, ungrab_time());
+ }
+ ret = TRUE;
+ }
+ return ret;
+}
+
+gint grab_server(gboolean grab)
+{
+ static guint sgrabs = 0;
+ if (grab) {
+ if (sgrabs++ == 0) {
+ XGrabServer(obt_display);
+ XSync(obt_display, FALSE);
+ }
+ } else if (sgrabs > 0) {
+ if (--sgrabs == 0) {
+ XUngrabServer(obt_display);
+ XFlush(obt_display);
+ }
+ }
+ return sgrabs;
+}
+
+void grab_startup(gboolean reconfig)
+{
+ guint i = 0;
+ guint num, caps, scroll;
+
+ num = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_NUMLOCK);
+ caps = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CAPSLOCK);
+ scroll = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SCROLLLOCK);
+
+ mask_list[i++] = 0;
+ mask_list[i++] = num;
+ mask_list[i++] = caps;
+ mask_list[i++] = scroll;
+ mask_list[i++] = num | caps;
+ mask_list[i++] = num | scroll;
+ mask_list[i++] = caps | scroll;
+ mask_list[i++] = num | caps | scroll;
+ g_assert(i == MASK_LIST_SIZE);
+
+ ic = obt_keyboard_context_new(obt_root(ob_screen), grab_window());
+}
+
+void grab_shutdown(gboolean reconfig)
+{
+ obt_keyboard_context_unref(ic);
+ ic = NULL;
+
+ if (reconfig) return;
+
+ while (ungrab_keyboard());
+ while (ungrab_pointer());
+ while (grab_server(FALSE));
+}
+
+void grab_button_full(guint button, guint state, Window win, guint mask,
+ gint pointer_mode, ObCursor cur)
+{
+ guint i;
+
+ /* can get BadAccess from these */
+ obt_display_ignore_errors(TRUE);
+ for (i = 0; i < MASK_LIST_SIZE; ++i)
+ XGrabButton(obt_display, button, state | mask_list[i], win, False,
+ mask, pointer_mode, GrabModeAsync, None, ob_cursor(cur));
+ obt_display_ignore_errors(FALSE);
+ if (obt_display_error_occured)
+ ob_debug("Failed to grab button %d modifiers %d", button, state);
+}
+
+void ungrab_button(guint button, guint state, Window win)
+{
+ guint i;
+
+ for (i = 0; i < MASK_LIST_SIZE; ++i)
+ XUngrabButton(obt_display, button, state | mask_list[i], win);
+}
+
+void grab_key(guint keycode, guint state, Window win, gint keyboard_mode)
+{
+ guint i;
+
+ /* can get BadAccess' from these */
+ obt_display_ignore_errors(TRUE);
+ for (i = 0; i < MASK_LIST_SIZE; ++i)
+ XGrabKey(obt_display, keycode, state | mask_list[i], win, FALSE,
+ GrabModeAsync, keyboard_mode);
+ obt_display_ignore_errors(FALSE);
+ if (obt_display_error_occured)
+ ob_debug("Failed to grab keycode %d modifiers %d", keycode, state);
+}
+
+void ungrab_all_keys(Window win)
+{
+ XUngrabKey(obt_display, AnyKey, AnyModifier, win);
+}
+
+void grab_key_passive_count(int change)
+{
+ if (grab_on_keyboard()) return;
+ passive_count += change;
+ if (passive_count < 0) passive_count = 0;
+}
+
+void ungrab_passive_key(void)
+{
+ /*ob_debug("ungrabbing %d passive grabs\n", passive_count);*/
+ if (passive_count) {
+ /* kill our passive grab */
+ XUngrabKeyboard(obt_display, event_time());
+ passive_count = 0;
+ }
+}
diff --git a/openbox/grab.h b/openbox/grab.h
new file mode 100644
index 0000000..c4fe17c
--- /dev/null
+++ b/openbox/grab.h
@@ -0,0 +1,58 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ grab.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __grab_h
+#define __grab_h
+
+#include "misc.h"
+#include "obt/keyboard.h"
+
+#include <glib.h>
+#include <X11/Xlib.h>
+
+void grab_startup(gboolean reconfig);
+void grab_shutdown(gboolean reconfig);
+
+ObtIC *grab_input_context(void);
+
+gboolean grab_keyboard_full(gboolean grab);
+/*! @param confine If true the pointer is confined to the screen */
+gboolean grab_pointer_full(gboolean grab, gboolean owner_events,
+ gboolean confine, ObCursor cur);
+gint grab_server(gboolean grab);
+
+#define grab_keyboard() grab_keyboard_full(TRUE)
+#define ungrab_keyboard() grab_keyboard_full(FALSE)
+#define grab_pointer(o,c,u) grab_pointer_full(TRUE, (o), (c), (u))
+#define ungrab_pointer() grab_pointer_full(FALSE, FALSE, FALSE, OB_CURSOR_NONE)
+
+gboolean grab_on_keyboard(void);
+gboolean grab_on_pointer(void);
+
+void grab_button_full(guint button, guint state, Window win, guint mask,
+ gint pointer_mode, ObCursor cursor);
+void ungrab_button(guint button, guint state, Window win);
+
+void grab_key(guint keycode, guint state, Window win, gint keyboard_mode);
+
+void ungrab_all_keys(Window win);
+
+void grab_key_passive_count(int change);
+void ungrab_passive_key(void);
+
+#endif
diff --git a/openbox/group.c b/openbox/group.c
new file mode 100644
index 0000000..0712bf4
--- /dev/null
+++ b/openbox/group.c
@@ -0,0 +1,66 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ group.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "group.h"
+#include "client.h"
+
+static GHashTable *group_map;
+
+static guint window_hash(Window *w) { return *w; }
+static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; }
+
+void group_startup(gboolean reconfig)
+{
+ if (reconfig) return;
+
+ group_map = g_hash_table_new((GHashFunc)window_hash,
+ (GEqualFunc)window_comp);
+}
+
+void group_shutdown(gboolean reconfig)
+{
+ if (reconfig) return;
+
+ g_hash_table_destroy(group_map);
+}
+
+ObGroup *group_add(Window leader, ObClient *client)
+{
+ ObGroup *self;
+
+ self = g_hash_table_lookup(group_map, &leader);
+ if (self == NULL) {
+ self = g_slice_new(ObGroup);
+ self->leader = leader;
+ self->members = NULL;
+ g_hash_table_insert(group_map, &self->leader, self);
+ }
+
+ self->members = g_slist_append(self->members, client);
+
+ return self;
+}
+
+void group_remove(ObGroup *self, ObClient *client)
+{
+ self->members = g_slist_remove(self->members, client);
+ if (self->members == NULL) {
+ g_hash_table_remove(group_map, &self->leader);
+ g_slice_free(ObGroup, self);
+ }
+}
diff --git a/openbox/group.h b/openbox/group.h
new file mode 100644
index 0000000..86b36b4
--- /dev/null
+++ b/openbox/group.h
@@ -0,0 +1,44 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ group.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __group_h
+#define __group_h
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+typedef struct _ObGroup ObGroup;
+
+struct _ObClient;
+
+struct _ObGroup
+{
+ Window leader;
+
+ /* list of clients */
+ GSList *members;
+};
+
+void group_startup(gboolean reconfig);
+void group_shutdown(gboolean reconfig);
+
+ObGroup *group_add(Window leader, struct _ObClient *client);
+
+void group_remove(ObGroup *self, struct _ObClient *client);
+
+#endif
diff --git a/openbox/keyboard.c b/openbox/keyboard.c
new file mode 100644
index 0000000..8f4424e
--- /dev/null
+++ b/openbox/keyboard.c
@@ -0,0 +1,340 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ keyboard.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "focus.h"
+#include "screen.h"
+#include "frame.h"
+#include "openbox.h"
+#include "event.h"
+#include "grab.h"
+#include "client.h"
+#include "actions.h"
+#include "menuframe.h"
+#include "config.h"
+#include "keytree.h"
+#include "keyboard.h"
+#include "translate.h"
+#include "moveresize.h"
+#include "popup.h"
+#include "gettext.h"
+#include "obt/keyboard.h"
+
+#include <glib.h>
+
+KeyBindingTree *keyboard_firstnode = NULL;
+static ObPopup *popup = NULL;
+static KeyBindingTree *curpos;
+static guint chain_timer = 0;
+
+static void grab_keys(gboolean grab)
+{
+ KeyBindingTree *p;
+
+ ungrab_all_keys(obt_root(ob_screen));
+
+ if (grab) {
+ p = curpos ? curpos->first_child : keyboard_firstnode;
+ while (p) {
+ if (p->key)
+ grab_key(p->key, p->state, obt_root(ob_screen),
+ GrabModeAsync);
+ p = p->next_sibling;
+ }
+ if (curpos)
+ grab_key(config_keyboard_reset_keycode,
+ config_keyboard_reset_state,
+ obt_root(ob_screen), GrabModeAsync);
+ }
+}
+
+static gboolean chain_timeout(gpointer data)
+{
+ keyboard_reset_chains(0);
+ return FALSE; /* don't repeat */
+}
+
+static void chain_done(gpointer data)
+{
+ chain_timer = 0;
+}
+
+static void set_curpos(KeyBindingTree *newpos)
+{
+ if (curpos == newpos) return;
+
+ grab_keys(FALSE);
+ curpos = newpos;
+ grab_keys(TRUE);
+
+ if (curpos != NULL) {
+ gchar *text = NULL;
+ GList *it;
+ const Rect *a;
+
+ for (it = curpos->keylist; it; it = g_list_next(it)) {
+ gchar *oldtext = text;
+ if (text == NULL)
+ text = g_strdup(it->data);
+ else
+ text = g_strconcat(text, " - ", it->data, NULL);
+ g_free(oldtext);
+ }
+
+ a = screen_physical_area_primary(FALSE);
+ popup_position(popup, NorthWestGravity, a->x + 10, a->y + 10);
+ /* 1 second delay for the popup to show */
+ popup_delay_show(popup, 1000, text);
+ g_free(text);
+ } else {
+ popup_hide(popup);
+ }
+}
+
+void keyboard_reset_chains(gint break_chroots)
+{
+ KeyBindingTree *p;
+
+ for (p = curpos; p; p = p->parent) {
+ if (p->chroot) {
+ if (break_chroots == 0) break; /* stop here */
+ if (break_chroots > 0)
+ --break_chroots;
+ }
+ }
+ set_curpos(p);
+}
+
+void keyboard_unbind_all(void)
+{
+ tree_destroy(keyboard_firstnode);
+ keyboard_firstnode = NULL;
+}
+
+void keyboard_chroot(GList *keylist)
+{
+ /* try do it in the existing tree. if we can't that means it is an empty
+ chroot binding. so add it to the tree then. */
+ if (!tree_chroot(keyboard_firstnode, keylist)) {
+ KeyBindingTree *tree;
+ if (!(tree = tree_build(keylist)))
+ return;
+ tree_chroot(tree, keylist);
+ tree_assimilate(tree);
+ }
+}
+
+gboolean keyboard_bind(GList *keylist, ObActionsAct *action)
+{
+ KeyBindingTree *tree, *t;
+ gboolean conflict;
+
+ g_assert(keylist != NULL);
+ g_assert(action != NULL);
+
+ if (!(tree = tree_build(keylist)))
+ return FALSE;
+
+ if ((t = tree_find(tree, &conflict)) != NULL) {
+ /* already bound to something, use the existing tree */
+ tree_destroy(tree);
+ tree = NULL;
+ } else
+ t = tree;
+
+ if (conflict) {
+ g_message(_("Conflict with key binding in config file"));
+ tree_destroy(tree);
+ return FALSE;
+ }
+
+ /* find the bottom node */
+ for (; t->first_child; t = t->first_child);
+
+ /* set the action */
+ t->actions = g_slist_append(t->actions, action);
+ /* assimilate this built tree into the main tree. assimilation
+ destroys/uses the tree */
+ if (tree) tree_assimilate(tree);
+
+ return TRUE;
+}
+
+#if 0
+gboolean keyboard_process_interactive_grab(const XEvent *e, ObClient **client)
+{
+ gboolean handled = FALSE;
+ gboolean done = FALSE;
+ gboolean cancel = FALSE;
+ guint mods;
+
+ mods = obt_keyboard_only_modmasks(ev->xkey.state);
+
+ if (istate.active) {
+ if ((e->type == KeyRelease && !(istate.state & mods))) {
+ done = TRUE;
+ handled = TRUE;
+ } else if (e->type == KeyPress) {
+ /*if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
+ done = TRUE;
+ else */if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
+ cancel = done = TRUE;
+ handled = TRUE;
+ }
+ } else if (e->type == ButtonPress) {
+ cancel = TRUE;
+ done = TRUE;
+ handled = FALSE;
+ }
+
+ if (done)
+ keyboard_interactive_end(e->xkey.state, cancel, e->xkey.time,TRUE);
+
+ if (handled)
+ *client = istate.client;
+ }
+
+ return handled;
+}
+#endif
+
+gboolean keyboard_event(ObClient *client, const XEvent *e)
+{
+ KeyBindingTree *p;
+ gboolean used;
+ guint mods;
+
+ if (e->type == KeyRelease) {
+ grab_key_passive_count(-1);
+ return FALSE;
+ }
+
+ g_assert(e->type == KeyPress);
+ grab_key_passive_count(1);
+
+ mods = obt_keyboard_only_modmasks(e->xkey.state);
+
+ if (e->xkey.keycode == config_keyboard_reset_keycode &&
+ mods == config_keyboard_reset_state)
+ {
+ if (chain_timer) g_source_remove(chain_timer);
+ keyboard_reset_chains(-1);
+ return TRUE;
+ }
+
+ used = FALSE;
+ if (curpos == NULL)
+ p = keyboard_firstnode;
+ else
+ p = curpos->first_child;
+ while (p) {
+ if (p->key == e->xkey.keycode && p->state == mods) {
+ /* if we hit a key binding, then close any open menus and run it */
+ if (menu_frame_visible)
+ menu_frame_hide_all();
+
+ if (p->first_child != NULL) { /* part of a chain */
+ if (chain_timer) g_source_remove(chain_timer);
+ /* 3 second timeout for chains */
+ chain_timer =
+ g_timeout_add_full(G_PRIORITY_DEFAULT,
+ 3000, chain_timeout, NULL,
+ chain_done);
+ set_curpos(p);
+ } else if (p->chroot) /* an empty chroot */
+ set_curpos(p);
+ else {
+ GSList *it;
+
+ for (it = p->actions; it; it = g_slist_next(it))
+ if (actions_act_is_interactive(it->data)) break;
+ if (it == NULL) /* reset if the actions are not interactive */
+ keyboard_reset_chains(0);
+
+ actions_run_acts(p->actions, OB_USER_ACTION_KEYBOARD_KEY,
+ e->xkey.state, e->xkey.x_root, e->xkey.y_root,
+ 0, OB_FRAME_CONTEXT_NONE, client);
+ }
+ break;
+ used = TRUE;
+ }
+ p = p->next_sibling;
+ }
+ return used;
+}
+
+static void node_rebind(KeyBindingTree *node)
+{
+ if (node->first_child) {
+ /* find leaf nodes */
+ node_rebind(node->first_child);
+
+ /* for internal nodes, add them to the tree if they
+ are a chroot, but do this after adding their
+ children */
+ if (node->chroot)
+ keyboard_chroot(node->keylist);
+ }
+ else {
+ /* for leaf nodes, rebind each action assigned to it */
+ while (node->actions) {
+ /* add each action, and remove them from the original tree so
+ they don't get free'd on us */
+ keyboard_bind(node->keylist, node->actions->data);
+ node->actions = g_slist_delete_link(node->actions, node->actions);
+ }
+
+ if (node->chroot)
+ keyboard_chroot(node->keylist);
+ }
+
+ /* go through each sibling */
+ if (node->next_sibling) node_rebind(node->next_sibling);
+}
+
+void keyboard_rebind(void)
+{
+ KeyBindingTree *old;
+
+ old = keyboard_firstnode;
+ keyboard_firstnode = NULL;
+ if (old)
+ node_rebind(old);
+
+ tree_destroy(old);
+ set_curpos(NULL);
+ grab_keys(TRUE);
+}
+
+void keyboard_startup(gboolean reconfig)
+{
+ grab_keys(TRUE);
+ popup = popup_new();
+ popup_set_text_align(popup, RR_JUSTIFY_CENTER);
+}
+
+void keyboard_shutdown(gboolean reconfig)
+{
+ if (chain_timer) g_source_remove(chain_timer);
+
+ keyboard_unbind_all();
+ set_curpos(NULL);
+
+ popup_free(popup);
+ popup = NULL;
+}
diff --git a/openbox/keyboard.h b/openbox/keyboard.h
new file mode 100644
index 0000000..c89f67e
--- /dev/null
+++ b/openbox/keyboard.h
@@ -0,0 +1,48 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ keyboard.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef ob__keybaord_h
+#define ob__keybaord_h
+
+#include "keytree.h"
+#include "frame.h"
+
+#include <glib.h>
+#include <X11/Xlib.h>
+
+struct _ObClient;
+struct _ObActionsAct;
+
+extern KeyBindingTree *keyboard_firstnode;
+
+void keyboard_startup(gboolean reconfig);
+void keyboard_shutdown(gboolean reconfig);
+
+void keyboard_rebind(void);
+
+void keyboard_chroot(GList *keylist);
+gboolean keyboard_bind(GList *keylist, struct _ObActionsAct *action);
+void keyboard_unbind_all(void);
+
+gboolean keyboard_event(struct _ObClient *client, const XEvent *e);
+/*! @param break_chroots how many chroots to break. -1 means to break them ALL!
+ */
+void keyboard_reset_chains(gint break_chroots);
+
+#endif
diff --git a/openbox/keytree.c b/openbox/keytree.c
new file mode 100644
index 0000000..93a0c7a
--- /dev/null
+++ b/openbox/keytree.c
@@ -0,0 +1,152 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ keytree.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "keyboard.h"
+#include "translate.h"
+#include "actions.h"
+#include <glib.h>
+
+void tree_destroy(KeyBindingTree *tree)
+{
+ KeyBindingTree *c;
+
+ while (tree) {
+ tree_destroy(tree->next_sibling);
+ c = tree->first_child;
+ if (c == NULL) {
+ GList *it;
+ GSList *sit;
+ for (it = tree->keylist; it != NULL; it = it->next)
+ g_free(it->data);
+ g_list_free(tree->keylist);
+ for (sit = tree->actions; sit != NULL; sit = sit->next)
+ actions_act_unref(sit->data);
+ g_slist_free(tree->actions);
+ }
+ g_slice_free(KeyBindingTree, tree);
+ tree = c;
+ }
+}
+
+KeyBindingTree *tree_build(GList *keylist)
+{
+ GList *it;
+ KeyBindingTree *ret = NULL, *p;
+
+ if (g_list_length(keylist) <= 0)
+ return NULL; /* nothing in the list.. */
+
+ for (it = g_list_last(keylist); it; it = g_list_previous(it)) {
+ GList *kit;
+
+ p = ret;
+ ret = g_slice_new0(KeyBindingTree);
+
+ for (kit = it; kit != NULL; kit = g_list_previous(kit))
+ ret->keylist = g_list_prepend(ret->keylist,
+ g_strdup(kit->data)); /* deep copy */
+ ret->first_child = p;
+ if (p != NULL) p->parent = ret;
+ translate_key(it->data, &ret->state, &ret->key);
+ }
+ return ret;
+}
+
+void tree_assimilate(KeyBindingTree *node)
+{
+ KeyBindingTree *a, *b, *tmp, *last;
+
+ if (keyboard_firstnode == NULL) {
+ /* there are no nodes at this level yet */
+ keyboard_firstnode = node;
+ } else {
+ a = keyboard_firstnode;
+ last = a;
+ b = node;
+ while (a) {
+ last = a;
+ /* check b->key != 0 for key bindings that didn't get translated */
+ if (!(a->state == b->state && a->key == b->key && b->key != 0)) {
+ a = a->next_sibling;
+ } else {
+ tmp = b;
+ b = b->first_child;
+ g_slice_free(KeyBindingTree, tmp);
+ a = a->first_child;
+ }
+ }
+ /* check b->key != 0, and save key bindings that didn't get translated
+ as siblings here */
+ if (!(last->state == b->state && last->key == b->key && b->key != 0)) {
+ last->next_sibling = b;
+ b->parent = last->parent;
+ } else {
+ last->first_child = b->first_child;
+ last->first_child->parent = last;
+ g_slice_free(KeyBindingTree, b);
+ }
+ }
+}
+
+KeyBindingTree *tree_find(KeyBindingTree *search, gboolean *conflict)
+{
+ KeyBindingTree *a, *b;
+
+ *conflict = FALSE;
+
+ a = keyboard_firstnode;
+ b = search;
+ while (a && b) {
+ /* check b->key != 0 for key bindings that didn't get translated, and
+ don't make them conflict with anything else so that they can all
+ live together in peace and harmony */
+ if (!(a->state == b->state && a->key == b->key && b->key != 0)) {
+ a = a->next_sibling;
+ } else {
+ if ((a->first_child == NULL) == (b->first_child == NULL)) {
+ if (a->first_child == NULL) {
+ /* found it! (return the actual node, not the search's) */
+ return a;
+ }
+ } else {
+ *conflict = TRUE;
+ return NULL; /* the chain status' don't match (conflict!) */
+ }
+ b = b->first_child;
+ a = a->first_child;
+ }
+ }
+ return NULL; /* it just isn't in here */
+}
+
+gboolean tree_chroot(KeyBindingTree *tree, GList *keylist)
+{
+ guint key, state;
+ translate_key(keylist->data, &state, &key);
+ while (tree != NULL && !(tree->state == state && tree->key == key))
+ tree = tree->next_sibling;
+ if (tree != NULL) {
+ if (keylist->next == NULL) {
+ tree->chroot = TRUE;
+ return TRUE;
+ } else
+ return tree_chroot(tree->first_child, keylist->next);
+ }
+ return FALSE;
+}
diff --git a/openbox/keytree.h b/openbox/keytree.h
new file mode 100644
index 0000000..66edc3c
--- /dev/null
+++ b/openbox/keytree.h
@@ -0,0 +1,45 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ keytree.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __plugin_keyboard_tree_h
+#define __plugin_keyboard_tree_h
+
+#include <glib.h>
+
+typedef struct KeyBindingTree {
+ guint state;
+ guint key;
+ GList *keylist;
+ GSList *actions; /* list of Action pointers */
+ gboolean chroot;
+
+ /* the level up in the tree */
+ struct KeyBindingTree *parent;
+ /* the next binding in the tree at the same level */
+ struct KeyBindingTree *next_sibling;
+ /* the first child of this binding (next binding in a chained sequence).*/
+ struct KeyBindingTree *first_child;
+} KeyBindingTree;
+
+void tree_destroy(KeyBindingTree *tree);
+KeyBindingTree *tree_build(GList *keylist);
+void tree_assimilate(KeyBindingTree *node);
+KeyBindingTree *tree_find(KeyBindingTree *search, gboolean *conflict);
+gboolean tree_chroot(KeyBindingTree *tree, GList *keylist);
+
+#endif
diff --git a/openbox/menu.c b/openbox/menu.c
new file mode 100644
index 0000000..1294c4c
--- /dev/null
+++ b/openbox/menu.c
@@ -0,0 +1,729 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ menu.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "debug.h"
+#include "menu.h"
+#include "openbox.h"
+#include "stacking.h"
+#include "grab.h"
+#include "client.h"
+#include "config.h"
+#include "actions.h"
+#include "screen.h"
+#include "menuframe.h"
+#include "keyboard.h"
+#include "geom.h"
+#include "misc.h"
+#include "client_menu.h"
+#include "client_list_menu.h"
+#include "client_list_combined_menu.h"
+#include "gettext.h"
+#include "obt/xml.h"
+#include "obt/paths.h"
+
+typedef struct _ObMenuParseState ObMenuParseState;
+
+struct _ObMenuParseState
+{
+ ObMenu *parent;
+ ObMenu *pipe_creator;
+};
+
+static GHashTable *menu_hash = NULL;
+static ObtXmlInst *menu_parse_inst;
+static ObMenuParseState menu_parse_state;
+static gboolean menu_can_hide = FALSE;
+static guint menu_timeout_id = 0;
+
+static void menu_destroy_hash_value(ObMenu *self);
+static void parse_menu_item(xmlNodePtr node, gpointer data);
+static void parse_menu_separator(xmlNodePtr node, gpointer data);
+static void parse_menu(xmlNodePtr node, gpointer data);
+static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut,
+ gchar **strippedlabel, guint *position,
+ gboolean *always_show);
+
+void menu_startup(gboolean reconfig)
+{
+ gboolean loaded = FALSE;
+ GSList *it;
+
+ menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+ (GDestroyNotify)menu_destroy_hash_value);
+
+ client_list_menu_startup(reconfig);
+ client_list_combined_menu_startup(reconfig);
+ client_menu_startup();
+
+ menu_parse_inst = obt_xml_instance_new();
+
+ menu_parse_state.parent = NULL;
+ menu_parse_state.pipe_creator = NULL;
+ obt_xml_register(menu_parse_inst, "menu", parse_menu, &menu_parse_state);
+ obt_xml_register(menu_parse_inst, "item", parse_menu_item,
+ &menu_parse_state);
+ obt_xml_register(menu_parse_inst, "separator",
+ parse_menu_separator, &menu_parse_state);
+
+ for (it = config_menu_files; it; it = g_slist_next(it)) {
+ if (obt_xml_load_config_file(menu_parse_inst,
+ "openbox",
+ 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);
+ }
+ if (!loaded) {
+ if (obt_xml_load_config_file(menu_parse_inst,
+ "openbox",
+ "menu.xml",
+ "openbox_menu"))
+ {
+ 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\""),
+ "menu.xml");
+ }
+
+ g_assert(menu_parse_state.parent == NULL);
+}
+
+void menu_shutdown(gboolean reconfig)
+{
+ obt_xml_instance_unref(menu_parse_inst);
+ menu_parse_inst = NULL;
+
+ client_list_menu_shutdown(reconfig);
+ client_list_combined_menu_shutdown(reconfig);
+
+ menu_frame_hide_all();
+ g_hash_table_destroy(menu_hash);
+ menu_hash = NULL;
+}
+
+static gboolean menu_pipe_submenu(gpointer key, gpointer val, gpointer data)
+{
+ ObMenu *menu = val;
+ return menu->pipe_creator != NULL;
+}
+
+static void clear_cache(gpointer key, gpointer val, gpointer data)
+{
+ ObMenu *menu = val;
+ if (menu->execute)
+ menu_clear_entries(menu);
+}
+
+void menu_clear_pipe_caches(void)
+{
+ /* delete any pipe menus' submenus */
+ g_hash_table_foreach_remove(menu_hash, menu_pipe_submenu, NULL);
+ /* empty the top level pipe menus */
+ g_hash_table_foreach(menu_hash, clear_cache, NULL);
+}
+
+void menu_pipe_execute(ObMenu *self)
+{
+ gchar *output;
+ GError *err = NULL;
+
+ if (!self->execute)
+ return;
+ if (self->entries) /* the entries are already created and cached */
+ return;
+
+ if (!g_spawn_command_line_sync(self->execute, &output, NULL, NULL, &err)) {
+ g_message(_("Failed to execute command for pipe-menu \"%s\": %s"),
+ self->execute, err->message);
+ g_error_free(err);
+ return;
+ }
+
+ if (obt_xml_load_mem(menu_parse_inst, output, strlen(output),
+ "openbox_pipe_menu"))
+ {
+ menu_parse_state.pipe_creator = self;
+ menu_parse_state.parent = self;
+ obt_xml_tree_from_root(menu_parse_inst);
+ obt_xml_close(menu_parse_inst);
+ } else {
+ g_message(_("Invalid output from pipe-menu \"%s\""), self->execute);
+ }
+
+ g_free(output);
+}
+
+static ObMenu* menu_from_name(gchar *name)
+{
+ ObMenu *self = NULL;
+
+ g_assert(name != NULL);
+
+ if (!(self = g_hash_table_lookup(menu_hash, name)))
+ g_message(_("Attempted to access menu \"%s\" but it does not exist"),
+ name);
+ return self;
+}
+
+#define VALID_SHORTCUT(c) (((c) >= '0' && (c) <= '9') || \
+ ((c) >= 'A' && (c) <= 'Z') || \
+ ((c) >= 'a' && (c) <= 'z'))
+
+static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut,
+ gchar **strippedlabel, guint *position,
+ gboolean *always_show)
+{
+ gunichar shortcut = 0;
+
+ *position = 0;
+ *always_show = FALSE;
+
+ g_assert(strippedlabel != NULL);
+
+ if (label == NULL) {
+ *strippedlabel = NULL;
+ } else {
+ gchar *i;
+ gboolean escape;
+
+ *strippedlabel = g_strdup(label);
+
+ /* if allow_shortcut is false, then you can't use the '_', instead you
+ have to just use the first valid character
+ */
+
+ /* allow __ to escape an underscore */
+ i = *strippedlabel;
+ do {
+ escape = FALSE;
+ i = strchr(i, '_');
+ if (i && *(i+1) == '_') {
+ gchar *j;
+
+ /* remove the escape '_' from the string */
+ for (j = i; *j != '\0'; ++j)
+ *j = *(j+1);
+
+ ++i;
+ escape = TRUE;
+ }
+ } while (escape);
+
+ if (allow_shortcut && i != NULL) {
+ /* there is an underscore in the string */
+
+ /* you have to use a printable ascii character for shortcuts
+ don't allow space either, so you can have like "a _ b"
+ */
+ if (VALID_SHORTCUT(*(i+1))) {
+ shortcut = g_unichar_tolower(g_utf8_get_char(i+1));
+ *position = i - *strippedlabel;
+ *always_show = TRUE;
+
+ /* remove the '_' from the string */
+ for (; *i != '\0'; ++i)
+ *i = *(i+1);
+ } else if (*(i+1) == '\0') {
+ /* no default shortcut if the '_' is the last character
+ (eg. "Exit_") for menu entries that you don't want
+ to be executed by mistake
+ */
+ *i = '\0';
+ }
+ } else {
+ /* there is no underscore, so find the first valid character to use
+ instead */
+
+ for (i = *strippedlabel; *i != '\0'; ++i)
+ if (VALID_SHORTCUT(*i)) {
+ *position = i - *strippedlabel;
+ shortcut = g_unichar_tolower(g_utf8_get_char(i));
+ break;
+ }
+ }
+ }
+ return shortcut;
+}
+
+static void parse_menu_item(xmlNodePtr node, gpointer data)
+{
+ ObMenuParseState *state = data;
+ gchar *label;
+ gchar *icon;
+ ObMenuEntry *e;
+
+ if (state->parent) {
+ /* Don't try to extract "icon" attribute if icons in user-defined
+ menus are not enabled. */
+
+ if (obt_xml_attr_string(node, "label", &label)) {
+ xmlNodePtr c;
+ GSList *acts = NULL;
+
+ c = obt_xml_find_node(node->children, "action");
+ while (c) {
+ ObActionsAct *action = actions_parse(c);
+ if (action)
+ acts = g_slist_append(acts, action);
+ c = obt_xml_find_node(c->next, "action");
+ }
+ e = menu_add_normal(state->parent, -1, label, acts, TRUE);
+
+ if (config_menu_show_icons &&
+ obt_xml_attr_string(node, "icon", &icon))
+ {
+ e->data.normal.icon = RrImageNewFromName(ob_rr_icons, icon);
+
+ if (e->data.normal.icon)
+ e->data.normal.icon_alpha = 0xff;
+
+ g_free(icon);
+ }
+ g_free(label);
+ }
+ }
+}
+
+static void parse_menu_separator(xmlNodePtr node, gpointer data)
+{
+ ObMenuParseState *state = data;
+
+ if (state->parent) {
+ gchar *label;
+
+ if (!obt_xml_attr_string(node, "label", &label))
+ label = NULL;
+
+ menu_add_separator(state->parent, -1, label);
+ g_free(label);
+ }
+}
+
+static void parse_menu(xmlNodePtr node, gpointer data)
+{
+ ObMenuParseState *state = data;
+ gchar *name = NULL, *title = NULL, *script = NULL;
+ ObMenu *menu;
+ ObMenuEntry *e;
+ gchar *icon;
+
+ if (!obt_xml_attr_string(node, "id", &name))
+ goto parse_menu_fail;
+
+ if (!g_hash_table_lookup(menu_hash, name)) {
+ if (!obt_xml_attr_string(node, "label", &title))
+ goto parse_menu_fail;
+
+ if ((menu = menu_new(name, title, TRUE, NULL))) {
+ menu->pipe_creator = state->pipe_creator;
+ if (obt_xml_attr_string(node, "execute", &script)) {
+ menu->execute = obt_paths_expand_tilde(script);
+ } else {
+ ObMenu *old;
+
+ old = state->parent;
+ state->parent = menu;
+ obt_xml_tree(menu_parse_inst, node->children);
+ state->parent = old;
+ }
+ }
+ }
+
+ if (state->parent) {
+ e = menu_add_submenu(state->parent, -1, name);
+
+ if (config_menu_show_icons &&
+ obt_xml_attr_string(node, "icon", &icon))
+ {
+ e->data.submenu.icon = RrImageNewFromName(ob_rr_icons, icon);
+
+ if (e->data.submenu.icon)
+ e->data.submenu.icon_alpha = 0xff;
+
+ g_free(icon);
+ }
+ }
+
+parse_menu_fail:
+ g_free(name);
+ g_free(title);
+ g_free(script);
+}
+
+ObMenu* menu_new(const gchar *name, const gchar *title,
+ gboolean allow_shortcut_selection, gpointer data)
+{
+ ObMenu *self;
+
+ self = g_slice_new0(ObMenu);
+ self->name = g_strdup(name);
+ self->data = data;
+
+ self->shortcut = parse_shortcut(title, allow_shortcut_selection,
+ &self->title, &self->shortcut_position,
+ &self->shortcut_always_show);
+
+ g_hash_table_replace(menu_hash, self->name, self);
+
+ /* Each menu has a single more_menu. When the menu spills past what
+ can fit on the screen, a new menu frame entry is created from this
+ more_menu, and a new menu frame for the submenu is created for this
+ menu, also pointing to the more_menu.
+
+ This can be done multiple times using the same more_menu.
+
+ more_menu->more_menu will always be NULL, since there is only 1 for
+ each menu. */
+ self->more_menu = g_slice_new0(ObMenu);
+ self->more_menu->name = _("More...");
+ self->more_menu->title = _("More...");
+ self->more_menu->data = data;
+ self->more_menu->shortcut = g_unichar_tolower(g_utf8_get_char("M"));
+
+ return self;
+}
+
+static void menu_destroy_hash_value(ObMenu *self)
+{
+ /* make sure its not visible */
+ {
+ GList *it;
+ ObMenuFrame *f;
+
+ for (it = menu_frame_visible; it; it = g_list_next(it)) {
+ f = it->data;
+ if (f->menu == self)
+ menu_frame_hide_all();
+ }
+ }
+
+ if (self->destroy_func)
+ self->destroy_func(self, self->data);
+
+ menu_clear_entries(self);
+ g_free(self->name);
+ g_free(self->title);
+ g_free(self->execute);
+ g_slice_free(ObMenu, self->more_menu);
+
+ g_slice_free(ObMenu, self);
+}
+
+void menu_free(ObMenu *menu)
+{
+ if (menu)
+ g_hash_table_remove(menu_hash, menu->name);
+}
+
+static gboolean menu_hide_delay_func(gpointer data)
+{
+ menu_can_hide = TRUE;
+ menu_timeout_id = 0;
+ return FALSE; /* no repeat */
+}
+
+void menu_show(gchar *name, gint x, gint y, gboolean mouse, ObClient *client)
+{
+ ObMenu *self;
+ ObMenuFrame *frame;
+
+ if (!(self = menu_from_name(name)) ||
+ grab_on_keyboard() || grab_on_pointer()) return;
+
+ /* if the requested menu is already the top visible menu, then don't
+ bother */
+ if (menu_frame_visible) {
+ frame = menu_frame_visible->data;
+ if (frame->menu == self)
+ return;
+ }
+
+ menu_frame_hide_all();
+
+ /* clear the pipe menus when showing a new menu */
+ menu_clear_pipe_caches();
+
+ frame = menu_frame_new(self, 0, client);
+ if (!menu_frame_show_topmenu(frame, x, y, mouse))
+ menu_frame_free(frame);
+ else {
+ if (!mouse) {
+ /* select the first entry if it's not a submenu and we opened
+ * the menu with the keyboard, and skip all headers */
+ GList *it = frame->entries;
+ while (it) {
+ ObMenuEntryFrame *e = it->data;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) {
+ menu_frame_select(frame, e, FALSE);
+ break;
+ } else if (e->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR)
+ it = g_list_next(it);
+ else
+ break;
+ }
+ }
+
+ /* reset the hide timer */
+ if (!mouse)
+ menu_can_hide = TRUE;
+ else {
+ menu_can_hide = FALSE;
+ if (menu_timeout_id) g_source_remove(menu_timeout_id);
+ menu_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ config_menu_hide_delay,
+ menu_hide_delay_func,
+ NULL, NULL);
+ }
+ }
+}
+
+gboolean menu_hide_delay_reached(void)
+{
+ return menu_can_hide;
+}
+
+static ObMenuEntry* menu_entry_new(ObMenu *menu, ObMenuEntryType type, gint id)
+{
+ ObMenuEntry *self;
+
+ g_assert(menu);
+
+ self = g_slice_new0(ObMenuEntry);
+ self->ref = 1;
+ self->type = type;
+ self->menu = menu;
+ self->id = id;
+
+ switch (type) {
+ case OB_MENU_ENTRY_TYPE_NORMAL:
+ self->data.normal.enabled = TRUE;
+ break;
+ case OB_MENU_ENTRY_TYPE_SUBMENU:
+ case OB_MENU_ENTRY_TYPE_SEPARATOR:
+ break;
+ }
+
+ return self;
+}
+
+void menu_entry_ref(ObMenuEntry *self)
+{
+ ++self->ref;
+}
+
+void menu_entry_unref(ObMenuEntry *self)
+{
+ if (self && --self->ref == 0) {
+ switch (self->type) {
+ case OB_MENU_ENTRY_TYPE_NORMAL:
+ RrImageUnref(self->data.normal.icon);
+ g_free(self->data.normal.label);
+ while (self->data.normal.actions) {
+ actions_act_unref(self->data.normal.actions->data);
+ self->data.normal.actions =
+ g_slist_delete_link(self->data.normal.actions,
+ self->data.normal.actions);
+ }
+ break;
+ case OB_MENU_ENTRY_TYPE_SUBMENU:
+ RrImageUnref(self->data.submenu.icon);
+ g_free(self->data.submenu.name);
+ break;
+ case OB_MENU_ENTRY_TYPE_SEPARATOR:
+ g_free(self->data.separator.label);
+ break;
+ }
+
+ g_slice_free(ObMenuEntry, self);
+ }
+}
+
+void menu_clear_entries(ObMenu *self)
+{
+#ifdef DEBUG
+ /* assert that the menu isn't visible */
+ {
+ GList *it;
+ ObMenuFrame *f;
+
+ for (it = menu_frame_visible; it; it = g_list_next(it)) {
+ f = it->data;
+ g_assert(f->menu != self);
+ }
+ }
+#endif
+
+ while (self->entries) {
+ menu_entry_unref(self->entries->data);
+ self->entries = g_list_delete_link(self->entries, self->entries);
+ }
+ self->more_menu->entries = self->entries; /* keep it in sync */
+}
+
+void menu_entry_remove(ObMenuEntry *self)
+{
+ self->menu->entries = g_list_remove(self->menu->entries, self);
+ menu_entry_unref(self);
+}
+
+ObMenuEntry* menu_add_normal(ObMenu *self, gint id, const gchar *label,
+ GSList *actions, gboolean allow_shortcut)
+{
+ ObMenuEntry *e;
+
+ e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_NORMAL, id);
+ e->data.normal.actions = actions;
+
+ menu_entry_set_label(e, label, allow_shortcut);
+
+ self->entries = g_list_append(self->entries, e);
+ self->more_menu->entries = self->entries; /* keep it in sync */
+ return e;
+}
+
+ObMenuEntry* menu_get_more(ObMenu *self, guint show_from)
+{
+ ObMenuEntry *e;
+ e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU, -1);
+ /* points to itself */
+ e->data.submenu.name = g_strdup(self->name);
+ e->data.submenu.submenu = self;
+ e->data.submenu.show_from = show_from;
+ return e;
+}
+
+ObMenuEntry* menu_add_submenu(ObMenu *self, gint id, const gchar *submenu)
+{
+ ObMenuEntry *e;
+
+ e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU, id);
+ e->data.submenu.name = g_strdup(submenu);
+
+ self->entries = g_list_append(self->entries, e);
+ self->more_menu->entries = self->entries; /* keep it in sync */
+ return e;
+}
+
+ObMenuEntry* menu_add_separator(ObMenu *self, gint id, const gchar *label)
+{
+ ObMenuEntry *e;
+
+ e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SEPARATOR, id);
+
+ menu_entry_set_label(e, label, FALSE);
+
+ self->entries = g_list_append(self->entries, e);
+ self->more_menu->entries = self->entries; /* keep it in sync */
+ return e;
+}
+
+void menu_set_show_func(ObMenu *self, ObMenuShowFunc func)
+{
+ self->show_func = func;
+}
+
+void menu_set_hide_func(ObMenu *self, ObMenuHideFunc func)
+{
+ self->hide_func = func;
+}
+
+void menu_set_update_func(ObMenu *self, ObMenuUpdateFunc func)
+{
+ self->update_func = func;
+}
+
+void menu_set_execute_func(ObMenu *self, ObMenuExecuteFunc func)
+{
+ self->execute_func = func;
+ self->more_menu->execute_func = func; /* keep it in sync */
+}
+
+void menu_set_cleanup_func(ObMenu *self, ObMenuCleanupFunc func)
+{
+ self->cleanup_func = func;
+}
+
+void menu_set_destroy_func(ObMenu *self, ObMenuDestroyFunc func)
+{
+ self->destroy_func = func;
+}
+
+void menu_set_place_func(ObMenu *self, ObMenuPlaceFunc func)
+{
+ self->place_func = func;
+}
+
+ObMenuEntry* menu_find_entry_id(ObMenu *self, gint id)
+{
+ ObMenuEntry *ret = NULL;
+ GList *it;
+
+ for (it = self->entries; it; it = g_list_next(it)) {
+ ObMenuEntry *e = it->data;
+
+ if (e->id == id) {
+ ret = e;
+ break;
+ }
+ }
+ return ret;
+}
+
+void menu_find_submenus(ObMenu *self)
+{
+ GList *it;
+
+ for (it = self->entries; it; it = g_list_next(it)) {
+ ObMenuEntry *e = it->data;
+
+ if (e->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ e->data.submenu.submenu = menu_from_name(e->data.submenu.name);
+ }
+}
+
+void menu_entry_set_label(ObMenuEntry *self, const gchar *label,
+ gboolean allow_shortcut)
+{
+ switch (self->type) {
+ case OB_MENU_ENTRY_TYPE_SEPARATOR:
+ g_free(self->data.separator.label);
+ self->data.separator.label = g_strdup(label);
+ break;
+ case OB_MENU_ENTRY_TYPE_NORMAL:
+ g_free(self->data.normal.label);
+ self->data.normal.shortcut =
+ parse_shortcut(label, allow_shortcut, &self->data.normal.label,
+ &self->data.normal.shortcut_position,
+ &self->data.normal.shortcut_always_show);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+void menu_show_all_shortcuts(ObMenu *self, gboolean show)
+{
+ self->show_all_shortcuts = show;
+}
diff --git a/openbox/menu.h b/openbox/menu.h
new file mode 100644
index 0000000..76cc238
--- /dev/null
+++ b/openbox/menu.h
@@ -0,0 +1,227 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ menu.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __menu_h
+#define __menu_h
+
+#include "window.h"
+#include "geom.h"
+#include "obrender/render.h"
+
+#include <glib.h>
+
+struct _ObClient;
+struct _ObMenuFrame;
+struct _ObMenuEntryFrame;
+
+typedef struct _ObMenu ObMenu;
+typedef struct _ObMenuEntry ObMenuEntry;
+typedef struct _ObNormalMenuEntry ObNormalMenuEntry;
+typedef struct _ObSubmenuMenuEntry ObSubmenuMenuEntry;
+typedef struct _ObSeparatorMenuEntry ObSeparatorMenuEntry;
+
+typedef void (*ObMenuShowFunc)(struct _ObMenuFrame *frame, gpointer data);
+typedef void (*ObMenuHideFunc)(struct _ObMenuFrame *frame, gpointer data);
+typedef gboolean (*ObMenuUpdateFunc)(struct _ObMenuFrame *frame,
+ gpointer data);
+typedef void (*ObMenuExecuteFunc)(struct _ObMenuEntry *entry,
+ struct _ObMenuFrame *frame,
+ struct _ObClient *client,
+ guint state, gpointer data);
+typedef void (*ObMenuCleanupFunc)(struct _ObMenu *menu, gpointer data);
+typedef void (*ObMenuDestroyFunc)(struct _ObMenu *menu, gpointer data);
+/*! @param x is the mouse x coordinate. on return it should be the x coordinate
+ for the menu
+ @param y is the mouse y coordinate. on return it should be the y coordinate
+ for the menu
+*/
+typedef void (*ObMenuPlaceFunc)(struct _ObMenuFrame *frame, gint *x, gint *y,
+ gboolean mouse, gpointer data);
+
+struct _ObMenu
+{
+ /* Name of the menu. Used in the showmenu action. */
+ gchar *name;
+ /* Displayed title */
+ gchar *title;
+ /*! The shortcut key that would be used to activate this menu if it was
+ displayed as a submenu */
+ gunichar shortcut;
+ /*! The shortcut's position in the string */
+ guint shortcut_position;
+ /*! If the shortcut was specified by & and should always be drawn */
+ gboolean shortcut_always_show;
+
+ /*! If the shortcut key should be shown in menu entries even when it
+ is the first character in the string */
+ gboolean show_all_shortcuts;
+
+ /* Command to execute to rebuild the menu */
+ gchar *execute;
+
+ /* ObMenuEntry list */
+ GList *entries;
+
+ /* plugin data */
+ gpointer data;
+
+ ObMenuShowFunc show_func;
+ ObMenuHideFunc hide_func;
+ ObMenuUpdateFunc update_func;
+ ObMenuExecuteFunc execute_func;
+ ObMenuCleanupFunc cleanup_func;
+ ObMenuDestroyFunc destroy_func;
+ ObMenuPlaceFunc place_func;
+
+ /* Pipe-menu parent, we get destroyed when it is destroyed */
+ ObMenu *pipe_creator;
+
+ /* The menu used as the destination for the "More..." entry for this menu*/
+ ObMenu *more_menu;
+};
+
+typedef enum
+{
+ OB_MENU_ENTRY_TYPE_NORMAL,
+ OB_MENU_ENTRY_TYPE_SUBMENU,
+ OB_MENU_ENTRY_TYPE_SEPARATOR
+} ObMenuEntryType;
+
+struct _ObNormalMenuEntry {
+ /* Icon stuff. If you set this, make sure you RrImageRef() it too. */
+ RrImage *icon;
+ gint icon_alpha;
+
+ gchar *label;
+ /*! The shortcut key that would be used to activate this menu entry */
+ gunichar shortcut;
+ /*! The shortcut's position in the string */
+ guint shortcut_position;
+ /*! If the shortcut was specified by & and should always be drawn */
+ gboolean shortcut_always_show;
+
+ /* state */
+ gboolean enabled;
+
+ /* List of ObActions */
+ GSList *actions;
+
+ /* Mask icon */
+ RrPixmapMask *mask;
+ RrColor *mask_normal_color;
+ RrColor *mask_selected_color;
+ RrColor *mask_disabled_color;
+ RrColor *mask_disabled_selected_color;
+
+ gpointer data;
+};
+
+struct _ObSubmenuMenuEntry {
+ /* Icon stuff. If you set this, make sure you RrImageRef() it too. */
+ RrImage *icon;
+ gint icon_alpha;
+
+ gchar *name;
+ ObMenu *submenu;
+
+ guint show_from;
+};
+
+struct _ObSeparatorMenuEntry {
+ gchar *label;
+};
+
+struct _ObMenuEntry
+{
+ guint ref;
+
+ ObMenuEntryType type;
+ ObMenu *menu;
+
+ gint id;
+
+ union u {
+ ObNormalMenuEntry normal;
+ ObSubmenuMenuEntry submenu;
+ ObSeparatorMenuEntry separator;
+ } data;
+};
+
+void menu_startup(gboolean reconfig);
+void menu_shutdown(gboolean reconfig);
+
+void menu_entry_ref(ObMenuEntry *self);
+void menu_entry_unref(ObMenuEntry *self);
+
+ObMenu* menu_new(const gchar *name, const gchar *title,
+ gboolean allow_shortcut_selection, gpointer data);
+void menu_free(ObMenu *menu);
+
+/*! Repopulate a pipe-menu by running its command */
+void menu_pipe_execute(ObMenu *self);
+/*! Clear a pipe-menu's entries */
+void menu_clear_pipe_caches(void);
+
+void menu_show_all_shortcuts(ObMenu *self, gboolean show);
+
+void menu_show(gchar *name, gint x, gint y, gboolean mouse,
+ struct _ObClient *client);
+gboolean menu_hide_delay_reached(void);
+
+/*! The show function is called right after a menu is shown */
+void menu_set_show_func(ObMenu *menu, ObMenuShowFunc func);
+/*! The hide function is called right before a menu is hidden */
+void menu_set_hide_func(ObMenu *menu, ObMenuHideFunc func);
+/*! The update function is called when the menu should refresh its
+ contents, generally right before it is shown */
+void menu_set_update_func(ObMenu *menu, ObMenuUpdateFunc func);
+/*! The execute function is called when a user chooses to execute an
+ entry in the menu */
+void menu_set_execute_func(ObMenu *menu, ObMenuExecuteFunc func);
+/*! The cleanup function is called after a menu is hidden, allowing it
+ to be cleaned up between uses */
+void menu_set_cleanup_func(ObMenu *menu, ObMenuCleanupFunc func);
+/*! The destroy function is called when the menu is being destroyed
+ permanently */
+void menu_set_destroy_func(ObMenu *menu, ObMenuDestroyFunc func);
+/*! The place function is called when the menu is being shown and allows
+ the menu to choose its initial position */
+void menu_set_place_func(ObMenu *menu, ObMenuPlaceFunc func);
+
+/* functions for building menus */
+/*! @param allow_shortcut this should be false when the label is coming from
+ outside data like window or desktop titles */
+ObMenuEntry* menu_add_normal(ObMenu *menu, gint id, const gchar *label,
+ GSList *actions, gboolean allow_shortcut);
+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);
+void menu_entry_remove(ObMenuEntry *self);
+
+void menu_entry_set_label(ObMenuEntry *self, const gchar *label,
+ gboolean allow_shortcut);
+
+ObMenuEntry* menu_find_entry_id(ObMenu *self, gint id);
+
+/* fills in the submenus, for use when a menu is being shown */
+void menu_find_submenus(ObMenu *self);
+
+ObMenuEntry* menu_get_more(ObMenu *menu, guint show_from);
+
+#endif
diff --git a/openbox/menuframe.c b/openbox/menuframe.c
new file mode 100644
index 0000000..6110045
--- /dev/null
+++ b/openbox/menuframe.c
@@ -0,0 +1,1387 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ menuframe.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "menuframe.h"
+#include "client.h"
+#include "menu.h"
+#include "screen.h"
+#include "actions.h"
+#include "event.h"
+#include "grab.h"
+#include "openbox.h"
+#include "config.h"
+#include "obt/prop.h"
+#include "obt/keyboard.h"
+#include "obrender/theme.h"
+
+#define PADDING 2
+#define MAX_MENU_WIDTH 400
+
+#define ITEM_HEIGHT (ob_rr_theme->menu_font_height + 2*PADDING)
+
+#define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask |\
+ LeaveWindowMask)
+#define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
+ ButtonPressMask | ButtonReleaseMask)
+
+GList *menu_frame_visible;
+GHashTable *menu_frame_map;
+
+static RrAppearance *a_sep;
+static guint submenu_show_timer = 0;
+static guint submenu_hide_timer = 0;
+
+static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
+ ObMenuFrame *frame);
+static void menu_entry_frame_free(ObMenuEntryFrame *self);
+static void menu_frame_update(ObMenuFrame *self);
+static gboolean submenu_show_timeout(gpointer data);
+static void menu_frame_hide(ObMenuFrame *self);
+
+static gboolean submenu_hide_timeout(gpointer data);
+
+static Window createWindow(Window parent, gulong mask,
+ XSetWindowAttributes *attrib)
+{
+ return XCreateWindow(obt_display, parent, 0, 0, 1, 1, 0,
+ RrDepth(ob_rr_inst), InputOutput,
+ RrVisual(ob_rr_inst), mask, attrib);
+}
+
+static void client_dest(ObClient *client, gpointer data)
+{
+ GList *it;
+
+ /* menus can be associated with a client, so null those refs since
+ we are disappearing now */
+ for (it = menu_frame_visible; it; it = g_list_next(it)) {
+ ObMenuFrame *f = it->data;
+ if (f->client == client)
+ f->client = NULL;
+ }
+}
+
+void menu_frame_startup(gboolean reconfig)
+{
+ gint i;
+
+ a_sep = RrAppearanceCopy(ob_rr_theme->a_clear);
+ RrAppearanceAddTextures(a_sep, ob_rr_theme->menu_sep_width);
+ for (i = 0; i < ob_rr_theme->menu_sep_width; ++i) {
+ a_sep->texture[i].type = RR_TEXTURE_LINE_ART;
+ a_sep->texture[i].data.lineart.color =
+ ob_rr_theme->menu_sep_color;
+ }
+
+ if (reconfig) return;
+
+ client_add_destroy_notify(client_dest, NULL);
+ menu_frame_map = g_hash_table_new(g_int_hash, g_int_equal);
+}
+
+void menu_frame_shutdown(gboolean reconfig)
+{
+ RrAppearanceFree(a_sep);
+
+ if (reconfig) return;
+
+ client_remove_destroy_notify(client_dest);
+ g_hash_table_destroy(menu_frame_map);
+}
+
+ObMenuFrame* menu_frame_new(ObMenu *menu, guint show_from, ObClient *client)
+{
+ ObMenuFrame *self;
+ XSetWindowAttributes attr;
+
+ self = g_slice_new0(ObMenuFrame);
+ self->obwin.type = OB_WINDOW_CLASS_MENUFRAME;
+ self->menu = menu;
+ self->selected = NULL;
+ self->client = client;
+ self->direction_right = TRUE;
+ self->show_from = show_from;
+
+ attr.event_mask = FRAME_EVENTMASK;
+ self->window = createWindow(obt_root(ob_screen),
+ CWEventMask, &attr);
+
+ /* make it a popup menu type window */
+ OBT_PROP_SET32(self->window, NET_WM_WINDOW_TYPE, ATOM,
+ OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_POPUP_MENU));
+
+ XSetWindowBorderWidth(obt_display, self->window, ob_rr_theme->mbwidth);
+ XSetWindowBorder(obt_display, self->window,
+ RrColorPixel(ob_rr_theme->menu_border_color));
+
+ self->a_items = RrAppearanceCopy(ob_rr_theme->a_menu);
+
+ window_add(&self->window, MENUFRAME_AS_WINDOW(self));
+ stacking_add(MENUFRAME_AS_WINDOW(self));
+
+ return self;
+}
+
+void menu_frame_free(ObMenuFrame *self)
+{
+ if (self) {
+ while (self->entries) {
+ menu_entry_frame_free(self->entries->data);
+ self->entries = g_list_delete_link(self->entries, self->entries);
+ }
+
+ stacking_remove(MENUFRAME_AS_WINDOW(self));
+ window_remove(self->window);
+
+ RrAppearanceFree(self->a_items);
+
+ XDestroyWindow(obt_display, self->window);
+
+ g_slice_free(ObMenuFrame, self);
+ }
+}
+
+ObtIC* menu_frame_ic(ObMenuFrame *self)
+{
+ /* menus are always used through a grab right now, so they can always use
+ the grab input context */
+ return grab_input_context();
+}
+
+static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
+ ObMenuFrame *frame)
+{
+ ObMenuEntryFrame *self;
+ XSetWindowAttributes attr;
+
+ self = g_slice_new0(ObMenuEntryFrame);
+ self->entry = entry;
+ self->frame = frame;
+
+ menu_entry_ref(entry);
+
+ attr.event_mask = ENTRY_EVENTMASK;
+ self->window = createWindow(self->frame->window, CWEventMask, &attr);
+ self->text = createWindow(self->window, 0, NULL);
+ g_hash_table_insert(menu_frame_map, &self->window, self);
+ g_hash_table_insert(menu_frame_map, &self->text, self);
+ if ((entry->type == OB_MENU_ENTRY_TYPE_NORMAL) ||
+ (entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) {
+ self->icon = createWindow(self->window, 0, NULL);
+ g_hash_table_insert(menu_frame_map, &self->icon, self);
+ }
+ if (entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
+ self->bullet = createWindow(self->window, 0, NULL);
+ g_hash_table_insert(menu_frame_map, &self->bullet, self);
+ }
+
+ XMapWindow(obt_display, self->window);
+ XMapWindow(obt_display, self->text);
+
+ window_add(&self->window, MENUFRAME_AS_WINDOW(self->frame));
+
+ return self;
+}
+
+static void menu_entry_frame_free(ObMenuEntryFrame *self)
+{
+ if (self) {
+ menu_entry_unref(self->entry);
+
+ window_remove(self->window);
+
+ XDestroyWindow(obt_display, self->text);
+ XDestroyWindow(obt_display, self->window);
+ g_hash_table_remove(menu_frame_map, &self->text);
+ g_hash_table_remove(menu_frame_map, &self->window);
+ if ((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) ||
+ (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) {
+ XDestroyWindow(obt_display, self->icon);
+ g_hash_table_remove(menu_frame_map, &self->icon);
+ }
+ if (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
+ XDestroyWindow(obt_display, self->bullet);
+ g_hash_table_remove(menu_frame_map, &self->bullet);
+ }
+
+ g_slice_free(ObMenuEntryFrame, self);
+ }
+}
+
+void menu_frame_move(ObMenuFrame *self, gint x, gint y)
+{
+ RECT_SET_POINT(self->area, x, y);
+ self->monitor = screen_find_monitor_point(x, y);
+ XMoveWindow(obt_display, self->window, self->area.x, self->area.y);
+}
+
+static void menu_frame_place_topmenu(ObMenuFrame *self, gint *x, gint *y)
+{
+ gint dx, dy;
+
+ if (config_menu_middle) {
+ gint myx;
+
+ myx = *x;
+ *y -= self->area.height / 2;
+
+ /* try to the right of the cursor */
+ menu_frame_move_on_screen(self, myx, *y, &dx, &dy);
+ self->direction_right = TRUE;
+ if (dx != 0) {
+ /* try to the left of the cursor */
+ myx = *x - self->area.width;
+ menu_frame_move_on_screen(self, myx, *y, &dx, &dy);
+ self->direction_right = FALSE;
+ }
+ if (dx != 0) {
+ /* if didnt fit on either side so just use what it says */
+ myx = *x;
+ menu_frame_move_on_screen(self, myx, *y, &dx, &dy);
+ self->direction_right = TRUE;
+ }
+ *x = myx + dx;
+ *y += dy;
+ } else {
+ gint myx, myy;
+
+ myx = *x;
+ myy = *y;
+
+ /* try to the bottom right of the cursor */
+ menu_frame_move_on_screen(self, myx, myy, &dx, &dy);
+ self->direction_right = TRUE;
+ if (dx != 0 || dy != 0) {
+ /* try to the bottom left of the cursor */
+ myx = *x - self->area.width;
+ myy = *y;
+ menu_frame_move_on_screen(self, myx, myy, &dx, &dy);
+ self->direction_right = FALSE;
+ }
+ if (dx != 0 || dy != 0) {
+ /* try to the top right of the cursor */
+ myx = *x;
+ myy = *y - self->area.height;
+ menu_frame_move_on_screen(self, myx, myy, &dx, &dy);
+ self->direction_right = TRUE;
+ }
+ if (dx != 0 || dy != 0) {
+ /* try to the top left of the cursor */
+ myx = *x - self->area.width;
+ myy = *y - self->area.height;
+ menu_frame_move_on_screen(self, myx, myy, &dx, &dy);
+ self->direction_right = FALSE;
+ }
+ if (dx != 0 || dy != 0) {
+ /* if didnt fit on either side so just use what it says */
+ myx = *x;
+ myy = *y;
+ menu_frame_move_on_screen(self, myx, myy, &dx, &dy);
+ self->direction_right = TRUE;
+ }
+ *x = myx + dx;
+ *y = myy + dy;
+ }
+}
+
+static void menu_frame_place_submenu(ObMenuFrame *self, gint *x, gint *y)
+{
+ gint overlapx, overlapy;
+ gint bwidth;
+
+ overlapx = ob_rr_theme->menu_overlap_x;
+ overlapy = ob_rr_theme->menu_overlap_y;
+ bwidth = ob_rr_theme->mbwidth;
+
+ if (self->direction_right)
+ *x = self->parent->area.x + self->parent->area.width -
+ overlapx - bwidth;
+ else
+ *x = self->parent->area.x - self->area.width + overlapx + bwidth;
+
+ *y = self->parent->area.y + self->parent_entry->area.y;
+ if (config_menu_middle)
+ *y -= (self->area.height - (bwidth * 2) - ITEM_HEIGHT) / 2;
+ else
+ *y += overlapy;
+}
+
+void menu_frame_move_on_screen(ObMenuFrame *self, gint x, gint y,
+ gint *dx, gint *dy)
+{
+ const Rect *a = NULL;
+ Rect search = self->area;
+ gint pos, half, monitor;
+
+ *dx = *dy = 0;
+ RECT_SET_POINT(search, x, y);
+
+ if (self->parent)
+ monitor = self->parent->monitor;
+ else
+ monitor = screen_find_monitor(&search);
+
+ a = screen_physical_area_monitor(monitor);
+
+ half = g_list_length(self->entries) / 2;
+ pos = g_list_index(self->entries, self->selected);
+
+ /* if in the bottom half then check this stuff first, will keep the bottom
+ edge of the menu visible */
+ if (pos > half) {
+ *dx = MAX(*dx, a->x - x);
+ *dy = MAX(*dy, a->y - y);
+ }
+ *dx = MIN(*dx, (a->x + a->width) - (x + self->area.width));
+ *dy = MIN(*dy, (a->y + a->height) - (y + self->area.height));
+ /* if in the top half then check this stuff last, will keep the top
+ edge of the menu visible */
+ if (pos <= half) {
+ *dx = MAX(*dx, a->x - x);
+ *dy = MAX(*dy, a->y - y);
+ }
+}
+
+static void menu_entry_frame_render(ObMenuEntryFrame *self)
+{
+ RrAppearance *item_a, *text_a;
+ gint th; /* temp */
+ ObMenu *sub;
+ ObMenuFrame *frame = self->frame;
+
+ switch (self->entry->type) {
+ case OB_MENU_ENTRY_TYPE_NORMAL:
+ case OB_MENU_ENTRY_TYPE_SUBMENU:
+ item_a = (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+ !self->entry->data.normal.enabled ?
+ /* disabled */
+ (self == self->frame->selected ?
+ ob_rr_theme->a_menu_disabled_selected :
+ ob_rr_theme->a_menu_disabled) :
+ /* enabled */
+ (self == self->frame->selected ?
+ ob_rr_theme->a_menu_selected :
+ ob_rr_theme->a_menu_normal));
+ th = ITEM_HEIGHT;
+ break;
+ case OB_MENU_ENTRY_TYPE_SEPARATOR:
+ if (self->entry->data.separator.label) {
+ item_a = ob_rr_theme->a_menu_title;
+ th = ob_rr_theme->menu_title_height;
+ } else {
+ item_a = ob_rr_theme->a_menu_normal;
+ th = ob_rr_theme->menu_sep_width +
+ 2*ob_rr_theme->menu_sep_paddingy;
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ RECT_SET_SIZE(self->area, self->frame->inner_w, th);
+ XResizeWindow(obt_display, self->window,
+ self->area.width, self->area.height);
+ item_a->surface.parent = self->frame->a_items;
+ item_a->surface.parentx = self->area.x;
+ item_a->surface.parenty = self->area.y;
+ RrPaint(item_a, self->window, self->area.width, self->area.height);
+
+ switch (self->entry->type) {
+ case OB_MENU_ENTRY_TYPE_NORMAL:
+ text_a = (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+ !self->entry->data.normal.enabled ?
+ /* disabled */
+ (self == self->frame->selected ?
+ ob_rr_theme->a_menu_text_disabled_selected :
+ ob_rr_theme->a_menu_text_disabled) :
+ /* enabled */
+ (self == self->frame->selected ?
+ ob_rr_theme->a_menu_text_selected :
+ ob_rr_theme->a_menu_text_normal));
+ text_a->texture[0].data.text.string = self->entry->data.normal.label;
+ if (self->entry->data.normal.shortcut &&
+ (self->frame->menu->show_all_shortcuts ||
+ self->entry->data.normal.shortcut_always_show ||
+ self->entry->data.normal.shortcut_position > 0))
+ {
+ text_a->texture[0].data.text.shortcut = TRUE;
+ text_a->texture[0].data.text.shortcut_pos =
+ self->entry->data.normal.shortcut_position;
+ } else
+ text_a->texture[0].data.text.shortcut = FALSE;
+ break;
+ case OB_MENU_ENTRY_TYPE_SUBMENU:
+ text_a = (self == self->frame->selected ?
+ ob_rr_theme->a_menu_text_selected :
+ ob_rr_theme->a_menu_text_normal);
+ sub = self->entry->data.submenu.submenu;
+ text_a->texture[0].data.text.string = sub ? sub->title : "";
+ if (sub && sub->shortcut && (self->frame->menu->show_all_shortcuts ||
+ sub->shortcut_always_show ||
+ sub->shortcut_position > 0))
+ {
+ text_a->texture[0].data.text.shortcut = TRUE;
+ text_a->texture[0].data.text.shortcut_pos = sub->shortcut_position;
+ } else
+ text_a->texture[0].data.text.shortcut = FALSE;
+ break;
+ case OB_MENU_ENTRY_TYPE_SEPARATOR:
+ if (self->entry->data.separator.label != NULL) {
+ text_a = ob_rr_theme->a_menu_text_title;
+ text_a->texture[0].data.text.string =
+ self->entry->data.separator.label;
+ }
+ else
+ text_a = ob_rr_theme->a_menu_text_normal;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ switch (self->entry->type) {
+ case OB_MENU_ENTRY_TYPE_NORMAL:
+ XMoveResizeWindow(obt_display, self->text,
+ self->frame->text_x, PADDING,
+ self->frame->text_w,
+ ITEM_HEIGHT - 2*PADDING);
+ text_a->surface.parent = item_a;
+ text_a->surface.parentx = self->frame->text_x;
+ text_a->surface.parenty = PADDING;
+ RrPaint(text_a, self->text, self->frame->text_w,
+ ITEM_HEIGHT - 2*PADDING);
+ break;
+ case OB_MENU_ENTRY_TYPE_SUBMENU:
+ XMoveResizeWindow(obt_display, self->text,
+ self->frame->text_x, PADDING,
+ self->frame->text_w - ITEM_HEIGHT,
+ ITEM_HEIGHT - 2*PADDING);
+ text_a->surface.parent = item_a;
+ text_a->surface.parentx = self->frame->text_x;
+ text_a->surface.parenty = PADDING;
+ RrPaint(text_a, self->text, self->frame->text_w - ITEM_HEIGHT,
+ ITEM_HEIGHT - 2*PADDING);
+ break;
+ case OB_MENU_ENTRY_TYPE_SEPARATOR:
+ if (self->entry->data.separator.label != NULL) {
+ /* labeled separator */
+ XMoveResizeWindow(obt_display, self->text,
+ ob_rr_theme->paddingx, ob_rr_theme->paddingy,
+ self->area.width - 2*ob_rr_theme->paddingx,
+ ob_rr_theme->menu_title_height -
+ 2*ob_rr_theme->paddingy);
+ text_a->surface.parent = item_a;
+ text_a->surface.parentx = ob_rr_theme->paddingx;
+ text_a->surface.parenty = ob_rr_theme->paddingy;
+ RrPaint(text_a, self->text,
+ self->area.width - 2*ob_rr_theme->paddingx,
+ ob_rr_theme->menu_title_height -
+ 2*ob_rr_theme->paddingy);
+ } else {
+ gint i;
+
+ /* unlabeled separator */
+ XMoveResizeWindow(obt_display, self->text, 0, 0,
+ self->area.width,
+ ob_rr_theme->menu_sep_width +
+ 2*ob_rr_theme->menu_sep_paddingy);
+
+ a_sep->surface.parent = item_a;
+ a_sep->surface.parentx = 0;
+ a_sep->surface.parenty = 0;
+ for (i = 0; i < ob_rr_theme->menu_sep_width; ++i) {
+ a_sep->texture[i].data.lineart.x1 =
+ ob_rr_theme->menu_sep_paddingx;
+ a_sep->texture[i].data.lineart.y1 =
+ ob_rr_theme->menu_sep_paddingy + i;
+ a_sep->texture[i].data.lineart.x2 =
+ self->area.width - ob_rr_theme->menu_sep_paddingx - 1;
+ a_sep->texture[i].data.lineart.y2 =
+ ob_rr_theme->menu_sep_paddingy + i;
+ }
+
+ RrPaint(a_sep, self->text, self->area.width,
+ ob_rr_theme->menu_sep_width +
+ 2*ob_rr_theme->menu_sep_paddingy);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) ||
+ (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) &&
+ self->entry->data.normal.icon)
+ {
+ RrAppearance *clear;
+
+ XMoveResizeWindow(obt_display, self->icon,
+ PADDING, frame->item_margin.top,
+ ITEM_HEIGHT - frame->item_margin.top
+ - frame->item_margin.bottom,
+ ITEM_HEIGHT - frame->item_margin.top
+ - frame->item_margin.bottom);
+
+ clear = ob_rr_theme->a_clear_tex;
+ RrAppearanceClearTextures(clear);
+ clear->texture[0].type = RR_TEXTURE_IMAGE;
+ clear->texture[0].data.image.image =
+ self->entry->data.normal.icon;
+ clear->texture[0].data.image.alpha =
+ self->entry->data.normal.icon_alpha;
+ clear->surface.parent = item_a;
+ clear->surface.parentx = PADDING;
+ clear->surface.parenty = frame->item_margin.top;
+ RrPaint(clear, self->icon,
+ ITEM_HEIGHT - frame->item_margin.top
+ - frame->item_margin.bottom,
+ ITEM_HEIGHT - frame->item_margin.top
+ - frame->item_margin.bottom);
+ XMapWindow(obt_display, self->icon);
+ } else if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+ self->entry->data.normal.mask)
+ {
+ RrColor *c;
+ RrAppearance *clear;
+
+ XMoveResizeWindow(obt_display, self->icon,
+ PADDING, frame->item_margin.top,
+ ITEM_HEIGHT - frame->item_margin.top
+ - frame->item_margin.bottom,
+ ITEM_HEIGHT - frame->item_margin.top
+ - frame->item_margin.bottom);
+
+ clear = ob_rr_theme->a_clear_tex;
+ RrAppearanceClearTextures(clear);
+ clear->texture[0].type = RR_TEXTURE_MASK;
+ clear->texture[0].data.mask.mask =
+ self->entry->data.normal.mask;
+
+ c = (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+ !self->entry->data.normal.enabled ?
+ /* disabled */
+ (self == self->frame->selected ?
+ self->entry->data.normal.mask_disabled_selected_color :
+ self->entry->data.normal.mask_disabled_color) :
+ /* enabled */
+ (self == self->frame->selected ?
+ self->entry->data.normal.mask_selected_color :
+ self->entry->data.normal.mask_normal_color));
+ clear->texture[0].data.mask.color = c;
+
+ clear->surface.parent = item_a;
+ clear->surface.parentx = PADDING;
+ clear->surface.parenty = frame->item_margin.top;
+ RrPaint(clear, self->icon,
+ ITEM_HEIGHT - frame->item_margin.top
+ - frame->item_margin.bottom,
+ ITEM_HEIGHT - frame->item_margin.top
+ - frame->item_margin.bottom);
+ XMapWindow(obt_display, self->icon);
+ } else
+ XUnmapWindow(obt_display, self->icon);
+
+ if (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
+ RrAppearance *bullet_a;
+ XMoveResizeWindow(obt_display, self->bullet,
+ self->frame->text_x + self->frame->text_w -
+ ITEM_HEIGHT + PADDING, PADDING,
+ ITEM_HEIGHT - 2*PADDING,
+ ITEM_HEIGHT - 2*PADDING);
+ bullet_a = (self == self->frame->selected ?
+ ob_rr_theme->a_menu_bullet_selected :
+ ob_rr_theme->a_menu_bullet_normal);
+ bullet_a->surface.parent = item_a;
+ bullet_a->surface.parentx =
+ self->frame->text_x + self->frame->text_w - ITEM_HEIGHT + PADDING;
+ bullet_a->surface.parenty = PADDING;
+ RrPaint(bullet_a, self->bullet,
+ ITEM_HEIGHT - 2*PADDING,
+ ITEM_HEIGHT - 2*PADDING);
+ XMapWindow(obt_display, self->bullet);
+ } else
+ XUnmapWindow(obt_display, self->bullet);
+
+ XFlush(obt_display);
+}
+
+/*! this code is taken from the menu_frame_render. if that changes, this won't
+ work.. */
+static gint menu_entry_frame_get_height(ObMenuEntryFrame *self,
+ gboolean first_entry,
+ gboolean last_entry)
+{
+ ObMenuEntryType t;
+ gint h = 0;
+
+ h += 2*PADDING;
+
+ if (self)
+ t = self->entry->type;
+ else
+ /* this is the More... entry, it's NORMAL type */
+ t = OB_MENU_ENTRY_TYPE_NORMAL;
+
+ switch (t) {
+ case OB_MENU_ENTRY_TYPE_NORMAL:
+ case OB_MENU_ENTRY_TYPE_SUBMENU:
+ h += ob_rr_theme->menu_font_height;
+ break;
+ case OB_MENU_ENTRY_TYPE_SEPARATOR:
+ if (self->entry->data.separator.label != NULL) {
+ h += ob_rr_theme->menu_title_height +
+ (ob_rr_theme->mbwidth - PADDING) * 2;
+
+ /* if the first entry is a labeled separator, then make its border
+ overlap with the menu's outside border */
+ if (first_entry)
+ h -= ob_rr_theme->mbwidth;
+ /* if the last entry is a labeled separator, then make its border
+ overlap with the menu's outside border */
+ if (last_entry)
+ h -= ob_rr_theme->mbwidth;
+ } else {
+ h += ob_rr_theme->menu_sep_width +
+ 2*ob_rr_theme->menu_sep_paddingy - PADDING * 2;
+ }
+ break;
+ }
+
+ return h;
+}
+
+void menu_frame_render(ObMenuFrame *self)
+{
+ gint w = 0, h = 0;
+ gint tw, th; /* temps */
+ GList *it;
+ gboolean has_icon = FALSE;
+ ObMenu *sub;
+ ObMenuEntryFrame *e;
+
+ /* find text dimensions */
+
+ STRUT_SET(self->item_margin, 0, 0, 0, 0);
+
+ if (self->entries) {
+ gint l, t, r, b;
+
+ e = self->entries->data;
+ ob_rr_theme->a_menu_text_normal->texture[0].data.text.string = "";
+ tw = RrMinWidth(ob_rr_theme->a_menu_text_normal);
+ tw += 2*PADDING;
+
+ th = ITEM_HEIGHT;
+
+ RrMargins(ob_rr_theme->a_menu_normal, &l, &t, &r, &b);
+ STRUT_SET(self->item_margin,
+ MAX(self->item_margin.left, l),
+ MAX(self->item_margin.top, t),
+ MAX(self->item_margin.right, r),
+ MAX(self->item_margin.bottom, b));
+ RrMargins(ob_rr_theme->a_menu_selected, &l, &t, &r, &b);
+ STRUT_SET(self->item_margin,
+ MAX(self->item_margin.left, l),
+ MAX(self->item_margin.top, t),
+ MAX(self->item_margin.right, r),
+ MAX(self->item_margin.bottom, b));
+ RrMargins(ob_rr_theme->a_menu_disabled, &l, &t, &r, &b);
+ STRUT_SET(self->item_margin,
+ MAX(self->item_margin.left, l),
+ MAX(self->item_margin.top, t),
+ MAX(self->item_margin.right, r),
+ MAX(self->item_margin.bottom, b));
+ RrMargins(ob_rr_theme->a_menu_disabled_selected, &l, &t, &r, &b);
+ STRUT_SET(self->item_margin,
+ MAX(self->item_margin.left, l),
+ MAX(self->item_margin.top, t),
+ MAX(self->item_margin.right, r),
+ MAX(self->item_margin.bottom, b));
+ }
+
+ /* render the entries */
+
+ for (it = self->entries; it; it = g_list_next(it)) {
+ RrAppearance *text_a;
+ e = it->data;
+
+ /* if the first entry is a labeled separator, then make its border
+ overlap with the menu's outside border */
+ if (it == self->entries &&
+ e->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR &&
+ e->entry->data.separator.label)
+ {
+ h -= ob_rr_theme->mbwidth;
+ }
+
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR &&
+ e->entry->data.separator.label)
+ {
+ e->border = ob_rr_theme->mbwidth;
+ }
+
+ RECT_SET_POINT(e->area, 0, h+e->border);
+ XMoveWindow(obt_display, e->window,
+ e->area.x-e->border, e->area.y-e->border);
+ XSetWindowBorderWidth(obt_display, e->window, e->border);
+ XSetWindowBorder(obt_display, e->window,
+ RrColorPixel(ob_rr_theme->menu_border_color));
+
+ text_a = (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+ !e->entry->data.normal.enabled ?
+ /* disabled */
+ (e == self->selected ?
+ ob_rr_theme->a_menu_text_disabled_selected :
+ ob_rr_theme->a_menu_text_disabled) :
+ /* enabled */
+ (e == self->selected ?
+ ob_rr_theme->a_menu_text_selected :
+ ob_rr_theme->a_menu_text_normal));
+ switch (e->entry->type) {
+ case OB_MENU_ENTRY_TYPE_NORMAL:
+ text_a->texture[0].data.text.string = e->entry->data.normal.label;
+ tw = RrMinWidth(text_a);
+ tw = MIN(tw, MAX_MENU_WIDTH);
+ th = ob_rr_theme->menu_font_height;
+
+ if (e->entry->data.normal.icon ||
+ e->entry->data.normal.mask)
+ has_icon = TRUE;
+ break;
+ case OB_MENU_ENTRY_TYPE_SUBMENU:
+ sub = e->entry->data.submenu.submenu;
+ text_a->texture[0].data.text.string = sub ? sub->title : "";
+ tw = RrMinWidth(text_a);
+ tw = MIN(tw, MAX_MENU_WIDTH);
+ th = ob_rr_theme->menu_font_height;
+
+ if (e->entry->data.normal.icon ||
+ e->entry->data.normal.mask)
+ has_icon = TRUE;
+
+ tw += ITEM_HEIGHT - PADDING;
+ break;
+ case OB_MENU_ENTRY_TYPE_SEPARATOR:
+ if (e->entry->data.separator.label != NULL) {
+ ob_rr_theme->a_menu_text_title->texture[0].data.text.string =
+ e->entry->data.separator.label;
+ tw = RrMinWidth(ob_rr_theme->a_menu_text_title) +
+ 2*ob_rr_theme->paddingx;
+ tw = MIN(tw, MAX_MENU_WIDTH);
+ th = ob_rr_theme->menu_title_height +
+ (ob_rr_theme->mbwidth - PADDING) *2;
+ } else {
+ tw = 0;
+ th = ob_rr_theme->menu_sep_width +
+ 2*ob_rr_theme->menu_sep_paddingy - 2*PADDING;
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ tw += 2*PADDING;
+ th += 2*PADDING;
+ w = MAX(w, tw);
+ h += th;
+ }
+
+ /* if the last entry is a labeled separator, then make its border
+ overlap with the menu's outside border */
+ it = g_list_last(self->entries);
+ e = it ? it->data : NULL;
+ if (e && e->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR &&
+ e->entry->data.separator.label)
+ {
+ h -= ob_rr_theme->mbwidth;
+ }
+
+ self->text_x = PADDING;
+ self->text_w = w;
+
+ if (self->entries) {
+ if (has_icon) {
+ w += ITEM_HEIGHT + PADDING;
+ self->text_x += ITEM_HEIGHT + PADDING;
+ }
+ }
+
+ if (!w) w = 10;
+ if (!h) h = 3;
+
+ XResizeWindow(obt_display, self->window, w, h);
+
+ self->inner_w = w;
+
+ RrPaint(self->a_items, self->window, w, h);
+
+ for (it = self->entries; it; it = g_list_next(it))
+ menu_entry_frame_render(it->data);
+
+ w += ob_rr_theme->mbwidth * 2;
+ h += ob_rr_theme->mbwidth * 2;
+
+ RECT_SET_SIZE(self->area, w, h);
+
+ XFlush(obt_display);
+}
+
+static void menu_frame_update(ObMenuFrame *self)
+{
+ GList *mit, *fit;
+ const Rect *a;
+ gint h;
+
+ menu_pipe_execute(self->menu);
+ menu_find_submenus(self->menu);
+
+ self->selected = NULL;
+
+ /* start at show_from */
+ mit = g_list_nth(self->menu->entries, self->show_from);
+
+ /* go through the menu's and frame's entries and connect the frame entries
+ to the menu entries */
+ for (fit = self->entries; mit && fit;
+ mit = g_list_next(mit), fit = g_list_next(fit))
+ {
+ ObMenuEntryFrame *f = fit->data;
+ f->entry = mit->data;
+ }
+
+ /* if there are more menu entries than in the frame, add them */
+ while (mit) {
+ ObMenuEntryFrame *e = menu_entry_frame_new(mit->data, self);
+ self->entries = g_list_append(self->entries, e);
+ mit = g_list_next(mit);
+ }
+
+ /* if there are more frame entries than menu entries then get rid of
+ them */
+ while (fit) {
+ GList *n = g_list_next(fit);
+ menu_entry_frame_free(fit->data);
+ self->entries = g_list_delete_link(self->entries, fit);
+ fit = n;
+ }
+
+ /* * make the menu fit on the screen */
+
+ /* calculate the height of the menu */
+ h = 0;
+ for (fit = self->entries; fit; fit = g_list_next(fit))
+ h += menu_entry_frame_get_height(fit->data,
+ fit == self->entries,
+ g_list_next(fit) == NULL);
+ /* add the border at the top and bottom */
+ h += ob_rr_theme->mbwidth * 2;
+
+ a = screen_physical_area_monitor(self->monitor);
+
+ if (h > a->height) {
+ GList *flast, *tmp;
+ gboolean last_entry = TRUE;
+
+ /* take the height of our More... entry into account */
+ h += menu_entry_frame_get_height(NULL, FALSE, TRUE);
+
+ /* start at the end of the entries */
+ flast = g_list_last(self->entries);
+
+ /* pull out all the entries from the frame that don't
+ fit on the screen, leaving at least 1 though */
+ while (h > a->height && g_list_previous(flast) != NULL) {
+ /* update the height, without this entry */
+ h -= menu_entry_frame_get_height(flast->data, FALSE, last_entry);
+
+ /* destroy the entry we're not displaying */
+ tmp = flast;
+ flast = g_list_previous(flast);
+ menu_entry_frame_free(tmp->data);
+ self->entries = g_list_delete_link(self->entries, tmp);
+
+ /* only the first one that we see is the last entry in the menu */
+ last_entry = FALSE;
+ };
+
+ {
+ ObMenuEntry *more_entry;
+ ObMenuEntryFrame *more_frame;
+ /* make the More... menu entry frame which will display in this
+ frame.
+ if self->menu->more_menu is NULL that means that this is already
+ More... menu, so just use ourself.
+ */
+ more_entry = menu_get_more((self->menu->more_menu ?
+ self->menu->more_menu :
+ self->menu),
+ /* continue where we left off */
+ self->show_from +
+ g_list_length(self->entries));
+ more_frame = menu_entry_frame_new(more_entry, self);
+ /* make it get deleted when the menu frame goes away */
+ menu_entry_unref(more_entry);
+
+ /* add our More... entry to the frame */
+ self->entries = g_list_append(self->entries, more_frame);
+ }
+ }
+
+ menu_frame_render(self);
+}
+
+static gboolean menu_frame_is_visible(ObMenuFrame *self)
+{
+ return !!(g_list_find(menu_frame_visible, self));
+}
+
+static gboolean menu_frame_show(ObMenuFrame *self)
+{
+ GList *it;
+
+ /* determine if the underlying menu is already visible */
+ for (it = menu_frame_visible; it; it = g_list_next(it)) {
+ ObMenuFrame *f = it->data;
+ if (f->menu == self->menu)
+ break;
+ }
+ if (!it) {
+ if (self->menu->update_func)
+ if (!self->menu->update_func(self, self->menu->data))
+ return FALSE;
+ }
+
+ if (menu_frame_visible == NULL) {
+ /* no menus shown yet */
+
+ /* grab the pointer in such a way as to pass through "owner events"
+ so that we can get enter/leave notifies in the menu. */
+ if (!grab_pointer(TRUE, FALSE, OB_CURSOR_POINTER))
+ return FALSE;
+ if (!grab_keyboard()) {
+ ungrab_pointer();
+ return FALSE;
+ }
+ }
+
+ menu_frame_update(self);
+
+ menu_frame_visible = g_list_prepend(menu_frame_visible, self);
+
+ if (self->menu->show_func)
+ self->menu->show_func(self, self->menu->data);
+
+ return TRUE;
+}
+
+gboolean menu_frame_show_topmenu(ObMenuFrame *self, gint x, gint y,
+ gboolean mouse)
+{
+ gint px, py;
+
+ if (menu_frame_is_visible(self))
+ return TRUE;
+ if (!menu_frame_show(self))
+ return FALSE;
+
+ if (self->menu->place_func)
+ self->menu->place_func(self, &x, &y, mouse, self->menu->data);
+ else
+ menu_frame_place_topmenu(self, &x, &y);
+
+ menu_frame_move(self, x, y);
+
+ XMapWindow(obt_display, self->window);
+
+ if (screen_pointer_pos(&px, &py)) {
+ ObMenuEntryFrame *e = menu_entry_frame_under(px, py);
+ if (e && e->frame == self)
+ e->ignore_enters++;
+ }
+
+ return TRUE;
+}
+
+/*! Stop hiding an open submenu.
+ @child The OnMenuFrame of the submenu to be hidden
+*/
+static void remove_submenu_hide_timeout(ObMenuFrame *child)
+{
+ if (submenu_hide_timer) g_source_remove(submenu_hide_timer);
+ submenu_hide_timer = 0;
+}
+
+gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent,
+ ObMenuEntryFrame *parent_entry)
+{
+ gint x, y, dx, dy;
+ gint px, py;
+
+ if (menu_frame_is_visible(self))
+ return TRUE;
+
+ self->monitor = parent->monitor;
+ self->parent = parent;
+ self->parent_entry = parent_entry;
+
+ /* set up parent's child to be us */
+ if ((parent->child) != self) {
+ if (parent->child)
+ menu_frame_hide(parent->child);
+ parent->child = self;
+ parent->child_entry = parent_entry;
+ }
+
+ if (!menu_frame_show(self))
+ return FALSE;
+
+ menu_frame_place_submenu(self, &x, &y);
+ menu_frame_move_on_screen(self, x, y, &dx, &dy);
+
+ if (dx != 0) {
+ /*try the other side */
+ self->direction_right = !self->direction_right;
+ menu_frame_place_submenu(self, &x, &y);
+ menu_frame_move_on_screen(self, x, y, &dx, &dy);
+ }
+ menu_frame_move(self, x + dx, y + dy);
+
+ XMapWindow(obt_display, self->window);
+
+ if (screen_pointer_pos(&px, &py)) {
+ ObMenuEntryFrame *e = menu_entry_frame_under(px, py);
+ if (e && e->frame == self)
+ e->ignore_enters++;
+ }
+
+ return TRUE;
+}
+
+static void menu_frame_hide(ObMenuFrame *self)
+{
+ ObMenu *const menu = self->menu;
+ GList *it = g_list_find(menu_frame_visible, self);
+ gulong ignore_start;
+
+ if (!it)
+ return;
+
+ if (menu->hide_func)
+ menu->hide_func(self, menu->data);
+
+ if (self->child)
+ menu_frame_hide(self->child);
+
+ if (self->parent) {
+ remove_submenu_hide_timeout(self);
+
+ self->parent->child = NULL;
+ self->parent->child_entry = NULL;
+ }
+ self->parent = NULL;
+ self->parent_entry = NULL;
+
+ menu_frame_visible = g_list_delete_link(menu_frame_visible, it);
+
+ if (menu_frame_visible == NULL) {
+ /* last menu shown */
+ ungrab_pointer();
+ ungrab_keyboard();
+ }
+
+ ignore_start = event_start_ignore_all_enters();
+ XUnmapWindow(obt_display, self->window);
+ event_end_ignore_all_enters(ignore_start);
+
+ menu_frame_free(self);
+
+ if (menu->cleanup_func)
+ menu->cleanup_func(menu, menu->data);
+}
+
+void menu_frame_hide_all(void)
+{
+ GList *it;
+
+ if (config_submenu_show_delay) {
+ /* remove any submenu open requests */
+ if (submenu_show_timer) g_source_remove(submenu_show_timer);
+ submenu_show_timer = 0;
+ }
+ if ((it = g_list_last(menu_frame_visible)))
+ menu_frame_hide(it->data);
+}
+
+ObMenuFrame* menu_frame_under(gint x, gint y)
+{
+ ObMenuFrame *ret = NULL;
+ GList *it;
+
+ for (it = menu_frame_visible; it; it = g_list_next(it)) {
+ ObMenuFrame *f = it->data;
+
+ if (RECT_CONTAINS(f->area, x, y)) {
+ ret = f;
+ break;
+ }
+ }
+ return ret;
+}
+
+ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y)
+{
+ ObMenuFrame *frame;
+ ObMenuEntryFrame *ret = NULL;
+ GList *it;
+
+ if ((frame = menu_frame_under(x, y))) {
+ x -= ob_rr_theme->mbwidth + frame->area.x;
+ y -= ob_rr_theme->mbwidth + frame->area.y;
+
+ for (it = frame->entries; it; it = g_list_next(it)) {
+ ObMenuEntryFrame *e = it->data;
+
+ if (RECT_CONTAINS(e->area, x, y)) {
+ ret = e;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+static gboolean submenu_show_timeout(gpointer data)
+{
+ g_assert(menu_frame_visible);
+ menu_entry_frame_show_submenu((ObMenuEntryFrame*)data);
+ return FALSE;
+}
+
+static gboolean submenu_hide_timeout(gpointer data)
+{
+ g_assert(menu_frame_visible);
+ menu_frame_hide((ObMenuFrame*)data);
+ return FALSE;
+}
+
+void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry,
+ gboolean immediate)
+{
+ ObMenuEntryFrame *old = self->selected;
+ ObMenuFrame *oldchild = self->child;
+ ObMenuEntryFrame *oldchild_entry = self->child_entry;
+
+ /* if the user selected a separator, ignore it and reselect what we had
+ selected before */
+ if (entry && entry->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR)
+ entry = old;
+
+ if (old == entry &&
+ (!old || old->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU))
+ return;
+
+ /* if the user left this menu but we have a submenu open, move the
+ selection back to that submenu */
+ if (!entry && oldchild_entry)
+ entry = oldchild_entry;
+
+ if (config_submenu_show_delay) {
+ /* remove any submenu open requests */
+ if (submenu_show_timer) g_source_remove(submenu_show_timer);
+ submenu_show_timer = 0;
+ }
+
+ self->selected = entry;
+
+ if (old)
+ menu_entry_frame_render(old);
+
+ if (oldchild_entry) {
+ /* There is an open submenu */
+ if (oldchild_entry == self->selected) {
+ /* The open submenu has been reselected, so stop hiding the
+ submenu */
+ remove_submenu_hide_timeout(oldchild);
+ }
+ else if (oldchild_entry == old) {
+ /* The open submenu was selected and is no longer, so hide the
+ submenu */
+ if (immediate || config_submenu_hide_delay == 0)
+ menu_frame_hide(oldchild);
+ else if (config_submenu_hide_delay > 0) {
+ if (submenu_hide_timer) g_source_remove(submenu_hide_timer);
+ submenu_hide_timer =
+ g_timeout_add_full(G_PRIORITY_DEFAULT,
+ config_submenu_hide_delay,
+ submenu_hide_timeout, oldchild, NULL);
+ }
+ }
+ }
+
+ if (self->selected) {
+ menu_entry_frame_render(self->selected);
+
+ if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
+ /* only show if the submenu isn't already showing */
+ if (oldchild_entry != self->selected) {
+ if (immediate || config_submenu_hide_delay == 0)
+ menu_entry_frame_show_submenu(self->selected);
+ else if (config_submenu_hide_delay > 0) {
+ if (submenu_show_timer)
+ g_source_remove(submenu_show_timer);
+ submenu_show_timer =
+ g_timeout_add_full(G_PRIORITY_DEFAULT,
+ config_submenu_show_delay,
+ submenu_show_timeout,
+ self->selected, NULL);
+ }
+ }
+ /* hide the grandchildren of this menu. and move the cursor to
+ the current menu */
+ else if (immediate && self->child && self->child->child) {
+ menu_frame_hide(self->child->child);
+ menu_frame_select(self->child, NULL, TRUE);
+ }
+ }
+ }
+}
+
+void menu_entry_frame_show_submenu(ObMenuEntryFrame *self)
+{
+ ObMenuFrame *f;
+
+ if (!self->entry->data.submenu.submenu) return;
+
+ f = menu_frame_new(self->entry->data.submenu.submenu,
+ self->entry->data.submenu.show_from,
+ self->frame->client);
+ /* pass our direction on to our child */
+ f->direction_right = self->frame->direction_right;
+
+ menu_frame_show_submenu(f, self->frame, self);
+}
+
+void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state)
+{
+ if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+ self->entry->data.normal.enabled)
+ {
+ /* grab all this shizzle, cuz when the menu gets hidden, 'self'
+ gets freed */
+ ObMenuEntry *entry = self->entry;
+ ObMenuExecuteFunc func = self->frame->menu->execute_func;
+ gpointer data = self->frame->menu->data;
+ GSList *acts = self->entry->data.normal.actions;
+ ObClient *client = self->frame->client;
+ ObMenuFrame *frame = self->frame;
+ guint mods = obt_keyboard_only_modmasks(state);
+
+ /* release grabs before executing the shit */
+ if (!(mods & ControlMask)) {
+ event_cancel_all_key_grabs();
+ frame = NULL;
+ }
+
+ if (func)
+ func(entry, frame, client, state, data);
+ else
+ actions_run_acts(acts, OB_USER_ACTION_MENU_SELECTION,
+ state, -1, -1, 0, OB_FRAME_CONTEXT_NONE, client);
+ }
+}
+
+void menu_frame_select_previous(ObMenuFrame *self)
+{
+ GList *it = NULL, *start;
+
+ if (self->entries) {
+ start = it = g_list_find(self->entries, self->selected);
+ while (TRUE) {
+ ObMenuEntryFrame *e;
+
+ it = it ? g_list_previous(it) : g_list_last(self->entries);
+ if (it == start)
+ break;
+
+ if (it) {
+ e = it->data;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ break;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
+ break;
+ }
+ }
+ }
+ menu_frame_select(self, it ? it->data : NULL, FALSE);
+}
+
+void menu_frame_select_next(ObMenuFrame *self)
+{
+ GList *it = NULL, *start;
+
+ if (self->entries) {
+ start = it = g_list_find(self->entries, self->selected);
+ while (TRUE) {
+ ObMenuEntryFrame *e;
+
+ it = it ? g_list_next(it) : self->entries;
+ if (it == start)
+ break;
+
+ if (it) {
+ e = it->data;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ break;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
+ break;
+ }
+ }
+ }
+ menu_frame_select(self, it ? it->data : NULL, FALSE);
+}
+
+void menu_frame_select_first(ObMenuFrame *self)
+{
+ GList *it = NULL;
+
+ if (self->entries) {
+ for (it = self->entries; it; it = g_list_next(it)) {
+ ObMenuEntryFrame *e = it->data;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ break;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
+ break;
+ }
+ }
+ menu_frame_select(self, it ? it->data : NULL, FALSE);
+}
+
+void menu_frame_select_last(ObMenuFrame *self)
+{
+ GList *it = NULL;
+
+ if (self->entries) {
+ for (it = g_list_last(self->entries); it; it = g_list_previous(it)) {
+ ObMenuEntryFrame *e = it->data;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ break;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
+ break;
+ }
+ }
+ menu_frame_select(self, it ? it->data : NULL, FALSE);
+}
diff --git a/openbox/menuframe.h b/openbox/menuframe.h
new file mode 100644
index 0000000..44c0256
--- /dev/null
+++ b/openbox/menuframe.h
@@ -0,0 +1,147 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ menuframe.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef ob__menuframe_h
+#define ob__menuframe_h
+
+#include "geom.h"
+#include "window.h"
+#include "obrender/render.h"
+#include "obt/keyboard.h"
+
+#include <glib.h>
+
+struct _ObClient;
+struct _ObMenu;
+struct _ObMenuEntry;
+
+typedef struct _ObMenuFrame ObMenuFrame;
+typedef struct _ObMenuEntryFrame ObMenuEntryFrame;
+
+extern GList *menu_frame_visible;
+
+struct _ObMenuFrame
+{
+ /* stuff to be an ObWindow */
+ ObWindow obwin;
+ Window window;
+
+ struct _ObMenu *menu;
+
+ /* The client that the visual instance of the menu is associated with for
+ its actions */
+ struct _ObClient *client;
+
+ ObMenuFrame *parent;
+ ObMenuEntryFrame *parent_entry;
+ ObMenuFrame *child;
+ ObMenuEntryFrame *child_entry;
+
+ GList *entries;
+ ObMenuEntryFrame *selected;
+
+ /* show entries from the menu starting at this index */
+ guint show_from;
+
+ /* If the submenus are being drawn to the right or the left */
+ gboolean direction_right;
+
+ /* On-screen area (including borders!) */
+ Rect area;
+ Strut item_margin;
+ gint inner_w; /* inside the borders */
+ gint item_h; /* height of all normal items */
+ gint text_x; /* offset at which the text appears in the items */
+ gint text_w; /* width of the text area in the items */
+ gint text_h; /* height of the items */
+
+ gint monitor; /* monitor on which to show the menu in xinerama */
+
+ /* We make a copy of this for each menu, so that we don't have to re-render
+ the background of the entire menu each time we render an item inside it.
+ */
+ RrAppearance *a_items;
+
+ gboolean got_press; /* don't allow a KeyRelease event to run things in the
+ menu until it has seen a KeyPress. this is to
+ avoid having the keybinding used to show the menu
+ end up running something inside the menu */
+ guint press_keycode; /* the KeyCode that was used in the last KeyPress */
+ gboolean press_doexec; /* if the upcoming KeyRelease should be used to
+ execute the menu item that was selected by the
+ KeyPress */
+};
+
+struct _ObMenuEntryFrame
+{
+ struct _ObMenuEntry *entry;
+ ObMenuFrame *frame;
+
+ guint ignore_enters;
+
+ Rect area;
+ gint border;
+
+ Window window;
+ Window icon;
+ Window text;
+ Window bullet;
+};
+
+extern GHashTable *menu_frame_map;
+
+void menu_frame_startup(gboolean reconfig);
+void menu_frame_shutdown(gboolean reconfig);
+
+ObMenuFrame* menu_frame_new(struct _ObMenu *menu,
+ guint show_from,
+ struct _ObClient *client);
+void menu_frame_free(ObMenuFrame *self);
+
+ObtIC* menu_frame_ic(ObMenuFrame *self);
+
+void menu_frame_move(ObMenuFrame *self, gint x, gint y);
+void menu_frame_move_on_screen(ObMenuFrame *self, gint x, gint y,
+ gint *dx, gint *dy);
+
+gboolean menu_frame_show_topmenu(ObMenuFrame *self, gint x, gint y,
+ gint button);
+gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent,
+ ObMenuEntryFrame *parent_entry);
+
+void menu_frame_hide_all(void);
+void menu_frame_hide_all_client(struct _ObClient *client);
+
+void menu_frame_render(ObMenuFrame *self);
+
+void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry,
+ gboolean immediate);
+void menu_frame_select_previous(ObMenuFrame *self);
+void menu_frame_select_next(ObMenuFrame *self);
+void menu_frame_select_first(ObMenuFrame *self);
+void menu_frame_select_last(ObMenuFrame *self);
+
+ObMenuFrame* menu_frame_under(gint x, gint y);
+ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y);
+
+void menu_entry_frame_show_submenu(ObMenuEntryFrame *self);
+
+void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state);
+
+#endif
diff --git a/openbox/misc.h b/openbox/misc.h
new file mode 100644
index 0000000..750dddd
--- /dev/null
+++ b/openbox/misc.h
@@ -0,0 +1,101 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ misc.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __ob__misc_h
+#define __ob__misc_h
+
+/*! The alpha value to use for icons of iconified windows in various places
+ like the focus cycle popup and client list menus.
+ Give iconic windows 7/16 alpha. A little under 50%.
+ */
+#define OB_ICONIC_ALPHA 0x70
+
+typedef enum
+{
+ OB_CURSOR_NONE,
+ OB_CURSOR_POINTER,
+ OB_CURSOR_BUSYPOINTER,
+ OB_CURSOR_BUSY,
+ OB_CURSOR_MOVE,
+ OB_CURSOR_NORTH,
+ OB_CURSOR_NORTHEAST,
+ OB_CURSOR_EAST,
+ OB_CURSOR_SOUTHEAST,
+ OB_CURSOR_SOUTH,
+ OB_CURSOR_SOUTHWEST,
+ OB_CURSOR_WEST,
+ OB_CURSOR_NORTHWEST,
+ OB_NUM_CURSORS
+} ObCursor;
+
+typedef enum
+{
+ OB_STATE_STARTING,
+ OB_STATE_RUNNING,
+ OB_STATE_EXITING,
+ OB_STATE_RECONFIGURING
+} ObState;
+
+typedef enum
+{
+ OB_DIRECTION_NORTH,
+ OB_DIRECTION_NORTHEAST,
+ OB_DIRECTION_EAST,
+ OB_DIRECTION_SOUTHEAST,
+ OB_DIRECTION_SOUTH,
+ OB_DIRECTION_SOUTHWEST,
+ OB_DIRECTION_WEST,
+ OB_DIRECTION_NORTHWEST
+} ObDirection;
+
+typedef enum
+{
+ OB_ORIENTATION_HORZ,
+ OB_ORIENTATION_VERT
+} ObOrientation;
+
+typedef enum
+{
+ OB_CORNER_TOPLEFT,
+ OB_CORNER_TOPRIGHT,
+ OB_CORNER_BOTTOMLEFT,
+ OB_CORNER_BOTTOMRIGHT
+} ObCorner;
+
+typedef enum {
+ OB_MOUSE_ACTION_PRESS,
+ OB_MOUSE_ACTION_RELEASE,
+ OB_MOUSE_ACTION_CLICK,
+ OB_MOUSE_ACTION_DOUBLE_CLICK,
+ OB_MOUSE_ACTION_MOTION,
+ OB_NUM_MOUSE_ACTIONS
+} ObMouseAction;
+
+typedef enum {
+ OB_USER_ACTION_NONE, /* being fired from inside another action and such */
+ OB_USER_ACTION_KEYBOARD_KEY,
+ OB_USER_ACTION_MOUSE_PRESS,
+ OB_USER_ACTION_MOUSE_RELEASE,
+ OB_USER_ACTION_MOUSE_CLICK,
+ OB_USER_ACTION_MOUSE_DOUBLE_CLICK,
+ OB_USER_ACTION_MOUSE_MOTION,
+ OB_USER_ACTION_MENU_SELECTION,
+ OB_NUM_USER_ACTIONS
+} ObUserAction;
+
+#endif
diff --git a/openbox/mouse.c b/openbox/mouse.c
new file mode 100644
index 0000000..2f0c8f5
--- /dev/null
+++ b/openbox/mouse.c
@@ -0,0 +1,413 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ mouse.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "openbox.h"
+#include "config.h"
+#include "actions.h"
+#include "event.h"
+#include "client.h"
+#include "grab.h"
+#include "frame.h"
+#include "translate.h"
+#include "mouse.h"
+#include "gettext.h"
+#include "obt/display.h"
+
+#include <glib.h>
+
+typedef struct {
+ guint state;
+ guint button;
+ GSList *actions[OB_NUM_MOUSE_ACTIONS]; /* lists of Action pointers */
+} ObMouseBinding;
+
+/* Array of GSList*s of ObMouseBinding*s. */
+static GSList *bound_contexts[OB_FRAME_NUM_CONTEXTS];
+/* TRUE when we have a grab on the pointer and need to replay the pointer event
+ to send it to other applications */
+static gboolean replay_pointer_needed;
+
+ObFrameContext mouse_button_frame_context(ObFrameContext context,
+ guint button,
+ guint state)
+{
+ GSList *it;
+ ObFrameContext x = context;
+
+ for (it = bound_contexts[context]; it; it = g_slist_next(it)) {
+ ObMouseBinding *b = it->data;
+
+ if (b->button == button && b->state == state)
+ return context;
+ }
+
+ switch (context) {
+ case OB_FRAME_CONTEXT_NONE:
+ case OB_FRAME_CONTEXT_DESKTOP:
+ case OB_FRAME_CONTEXT_CLIENT:
+ case OB_FRAME_CONTEXT_TITLEBAR:
+ case OB_FRAME_CONTEXT_FRAME:
+ case OB_FRAME_CONTEXT_MOVE_RESIZE:
+ case OB_FRAME_CONTEXT_LEFT:
+ case OB_FRAME_CONTEXT_RIGHT:
+ case OB_FRAME_CONTEXT_DOCK:
+ break;
+ case OB_FRAME_CONTEXT_ROOT:
+ x = OB_FRAME_CONTEXT_DESKTOP;
+ break;
+ case OB_FRAME_CONTEXT_BOTTOM:
+ case OB_FRAME_CONTEXT_BLCORNER:
+ case OB_FRAME_CONTEXT_BRCORNER:
+ x = OB_FRAME_CONTEXT_BOTTOM;
+ break;
+ case OB_FRAME_CONTEXT_TLCORNER:
+ case OB_FRAME_CONTEXT_TRCORNER:
+ case OB_FRAME_CONTEXT_TOP:
+ case OB_FRAME_CONTEXT_MAXIMIZE:
+ case OB_FRAME_CONTEXT_ALLDESKTOPS:
+ case OB_FRAME_CONTEXT_SHADE:
+ case OB_FRAME_CONTEXT_ICONIFY:
+ case OB_FRAME_CONTEXT_ICON:
+ case OB_FRAME_CONTEXT_CLOSE:
+ x = OB_FRAME_CONTEXT_TITLEBAR;
+ break;
+ case OB_FRAME_NUM_CONTEXTS:
+ g_assert_not_reached();
+ }
+
+ /* allow for multiple levels of fall-through */
+ if (x != context)
+ return mouse_button_frame_context(x, button, state);
+ else
+ return x;
+}
+
+void mouse_grab_for_client(ObClient *client, gboolean grab)
+{
+ gint i;
+ GSList *it;
+
+ for (i = 0; i < OB_FRAME_NUM_CONTEXTS; ++i)
+ for (it = bound_contexts[i]; it; it = g_slist_next(it)) {
+ /* grab/ungrab the button */
+ ObMouseBinding *b = it->data;
+ Window win;
+ gint mode;
+ guint mask;
+
+ if (FRAME_CONTEXT(i, client)) {
+ win = client->frame->window;
+ mode = GrabModeAsync;
+ mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask;
+ } else if (CLIENT_CONTEXT(i, client)) {
+ win = client->window;
+ mode = GrabModeSync; /* this is handled in event */
+ mask = ButtonPressMask; /* can't catch more than this with Sync
+ mode the release event is
+ manufactured in event() */
+ } else continue;
+
+ if (grab)
+ grab_button_full(b->button, b->state, win, mask, mode,
+ OB_CURSOR_NONE);
+ else
+ ungrab_button(b->button, b->state, win);
+ }
+}
+
+static void grab_all_clients(gboolean grab)
+{
+ GList *it;
+
+ for (it = client_list; it; it = g_list_next(it))
+ mouse_grab_for_client(it->data, grab);
+}
+
+void mouse_unbind_all(void)
+{
+ gint i;
+ GSList *it;
+
+ for(i = 0; i < OB_FRAME_NUM_CONTEXTS; ++i) {
+ for (it = bound_contexts[i]; it; it = g_slist_next(it)) {
+ ObMouseBinding *b = it->data;
+ gint j;
+
+ for (j = 0; j < OB_NUM_MOUSE_ACTIONS; ++j) {
+ GSList *jt;
+
+ for (jt = b->actions[j]; jt; jt = g_slist_next(jt))
+ actions_act_unref(jt->data);
+ g_slist_free(b->actions[j]);
+ }
+ g_slice_free(ObMouseBinding, b);
+ }
+ g_slist_free(bound_contexts[i]);
+ bound_contexts[i] = NULL;
+ }
+}
+
+static ObUserAction mouse_action_to_user_action(ObMouseAction a)
+{
+ switch (a) {
+ case OB_MOUSE_ACTION_PRESS: return OB_USER_ACTION_MOUSE_PRESS;
+ case OB_MOUSE_ACTION_RELEASE: return OB_USER_ACTION_MOUSE_RELEASE;
+ case OB_MOUSE_ACTION_CLICK: return OB_USER_ACTION_MOUSE_CLICK;
+ case OB_MOUSE_ACTION_DOUBLE_CLICK:
+ return OB_USER_ACTION_MOUSE_DOUBLE_CLICK;
+ case OB_MOUSE_ACTION_MOTION: return OB_USER_ACTION_MOUSE_MOTION;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static gboolean fire_binding(ObMouseAction a, ObFrameContext context,
+ ObClient *c, guint state,
+ guint button, gint x, gint y)
+{
+ GSList *it;
+ ObMouseBinding *b;
+
+ for (it = bound_contexts[context]; it; it = g_slist_next(it)) {
+ b = it->data;
+ if (b->state == state && b->button == button)
+ break;
+ }
+ /* if not bound, then nothing to do! */
+ if (it == NULL) return FALSE;
+
+ actions_run_acts(b->actions[a], mouse_action_to_user_action(a),
+ state, x, y, button, context, c);
+ return TRUE;
+}
+
+void mouse_replay_pointer(void)
+{
+ if (replay_pointer_needed) {
+ /* replay the pointer event before any windows move */
+ XAllowEvents(obt_display, ReplayPointer, event_time());
+ replay_pointer_needed = FALSE;
+ }
+}
+
+gboolean mouse_event(ObClient *client, XEvent *e)
+{
+ static Time ltime;
+ static guint button = 0, state = 0, lbutton = 0;
+ static Window lwindow = None;
+ static gint px, py, pwx = -1, pwy = -1, lx = -10, ly = -10;
+ gboolean used = FALSE;
+
+ ObFrameContext context;
+ gboolean click = FALSE;
+ gboolean dclick = FALSE;
+
+ switch (e->type) {
+ case ButtonPress:
+ context = frame_context(client, e->xbutton.window,
+ e->xbutton.x, e->xbutton.y);
+ context = mouse_button_frame_context(context, e->xbutton.button,
+ e->xbutton.state);
+
+ px = e->xbutton.x_root;
+ py = e->xbutton.y_root;
+ if (!button) pwx = e->xbutton.x;
+ if (!button) pwy = e->xbutton.y;
+ button = e->xbutton.button;
+ state = e->xbutton.state;
+
+ /* if the binding was in a client context, then we need to call
+ XAllowEvents with ReplayPointer at some point, to send the event
+ through to the client. when this happens though depends. if
+ windows are going to be moved on screen, then the click will end
+ up going somewhere wrong, set that we need it, and if nothing
+ else causes the replay pointer to be run, then we will do it
+ after all the actions are finished.
+
+ (We do it after all the actions because FocusIn interrupts
+ dragging for kdesktop, so if we send the button event now, and
+ then they get a focus event after, it breaks. Instead, wait to send
+ the button press until after the actions when possible.)
+ */
+ if (CLIENT_CONTEXT(context, client))
+ replay_pointer_needed = TRUE;
+
+ used = fire_binding(OB_MOUSE_ACTION_PRESS, context,
+ client, e->xbutton.state,
+ e->xbutton.button,
+ e->xbutton.x_root, e->xbutton.y_root) || used;
+
+ /* if the bindings grab the pointer, there won't be a ButtonRelease
+ event for us */
+ if (grab_on_pointer())
+ button = 0;
+
+ /* replay the pointer event if it hasn't been replayed yet (i.e. no
+ windows were moved) */
+ mouse_replay_pointer();
+
+ /* in the client context, we won't get a button release because of the
+ way it is grabbed, so just fake one */
+ if (!CLIENT_CONTEXT(context, client))
+ break;
+
+ case ButtonRelease:
+ /* use where the press occured in the window */
+ context = frame_context(client, e->xbutton.window, pwx, pwy);
+ context = mouse_button_frame_context(context, e->xbutton.button,
+ e->xbutton.state);
+
+ if (e->xbutton.button == button)
+ pwx = pwy = -1;
+
+ if (e->xbutton.button == button) {
+ /* clicks are only valid if its released over the window */
+ gint junk1, junk2;
+ Window wjunk;
+ guint ujunk, b, w, h;
+ /* this can cause errors to occur when the window closes */
+ obt_display_ignore_errors(TRUE);
+ junk1 = XGetGeometry(obt_display, e->xbutton.window,
+ &wjunk, &junk1, &junk2, &w, &h, &b, &ujunk);
+ obt_display_ignore_errors(FALSE);
+ if (junk1) {
+ if (e->xbutton.x >= (signed)-b &&
+ e->xbutton.y >= (signed)-b &&
+ e->xbutton.x < (signed)(w+b) &&
+ e->xbutton.y < (signed)(h+b))
+ {
+ click = TRUE;
+ /* double clicks happen if there were 2 in a row! */
+ if (lbutton == button &&
+ lwindow == e->xbutton.window &&
+ e->xbutton.time - config_mouse_dclicktime <=
+ ltime &&
+ ABS(e->xbutton.x - lx) < 8 &&
+ ABS(e->xbutton.y - ly) < 8)
+ {
+ dclick = TRUE;
+ lbutton = 0;
+ } else {
+ lbutton = button;
+ lwindow = e->xbutton.window;
+ lx = e->xbutton.x;
+ ly = e->xbutton.y;
+ }
+ } else {
+ lbutton = 0;
+ lwindow = None;
+ }
+ }
+
+ button = 0;
+ state = 0;
+ ltime = e->xbutton.time;
+ }
+ used = fire_binding(OB_MOUSE_ACTION_RELEASE, context,
+ client, e->xbutton.state,
+ e->xbutton.button,
+ e->xbutton.x_root,
+ e->xbutton.y_root) || used;
+ if (click)
+ used = fire_binding(OB_MOUSE_ACTION_CLICK, context,
+ client, e->xbutton.state,
+ e->xbutton.button,
+ e->xbutton.x_root,
+ e->xbutton.y_root) || used;
+ if (dclick)
+ used = fire_binding(OB_MOUSE_ACTION_DOUBLE_CLICK, context,
+ client, e->xbutton.state,
+ e->xbutton.button,
+ e->xbutton.x_root,
+ e->xbutton.y_root) || used;
+ break;
+
+ case MotionNotify:
+ if (button) {
+ context = frame_context(client, e->xmotion.window, pwx, pwy);
+ context = mouse_button_frame_context(context, button, state);
+
+ if (ABS(e->xmotion.x_root - px) >= config_mouse_threshold ||
+ ABS(e->xmotion.y_root - py) >= config_mouse_threshold) {
+
+ /* You can't drag on buttons */
+ if (context == OB_FRAME_CONTEXT_MAXIMIZE ||
+ context == OB_FRAME_CONTEXT_ALLDESKTOPS ||
+ context == OB_FRAME_CONTEXT_SHADE ||
+ context == OB_FRAME_CONTEXT_ICONIFY ||
+ context == OB_FRAME_CONTEXT_ICON ||
+ context == OB_FRAME_CONTEXT_CLOSE)
+ break;
+
+ used = fire_binding(OB_MOUSE_ACTION_MOTION, context,
+ client, state, button, px, py);
+ button = 0;
+ state = 0;
+ }
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ return used;
+}
+
+gboolean mouse_bind(const gchar *buttonstr, ObFrameContext context,
+ ObMouseAction mact, ObActionsAct *action)
+{
+ guint state, button;
+ ObMouseBinding *b;
+ GSList *it;
+
+ g_assert(context != OB_FRAME_CONTEXT_NONE);
+
+ if (!translate_button(buttonstr, &state, &button)) {
+ g_message(_("Invalid button \"%s\" in mouse binding"), buttonstr);
+ return FALSE;
+ }
+
+ for (it = bound_contexts[context]; it; it = g_slist_next(it)) {
+ b = it->data;
+ if (b->state == state && b->button == button) {
+ b->actions[mact] = g_slist_append(b->actions[mact], action);
+ return TRUE;
+ }
+ }
+
+ /* add the binding */
+ b = g_slice_new0(ObMouseBinding);
+ b->state = state;
+ b->button = button;
+ b->actions[mact] = g_slist_append(NULL, action);
+ bound_contexts[context] = g_slist_append(bound_contexts[context], b);
+
+ return TRUE;
+}
+
+void mouse_startup(gboolean reconfig)
+{
+ grab_all_clients(TRUE);
+}
+
+void mouse_shutdown(gboolean reconfig)
+{
+ grab_all_clients(FALSE);
+ mouse_unbind_all();
+}
diff --git a/openbox/mouse.h b/openbox/mouse.h
new file mode 100644
index 0000000..de4c0ec
--- /dev/null
+++ b/openbox/mouse.h
@@ -0,0 +1,48 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ mouse.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef ob__mouse_h
+#define ob__mouse_h
+
+#include "frame.h"
+#include "misc.h"
+
+#include <X11/Xlib.h>
+
+struct _ObActionsAct;
+
+void mouse_startup(gboolean reconfig);
+void mouse_shutdown(gboolean reconfig);
+
+gboolean mouse_bind(const gchar *buttonstr, ObFrameContext context,
+ ObMouseAction mact, struct _ObActionsAct *action);
+void mouse_unbind_all(void);
+
+gboolean mouse_event(struct _ObClient *client, XEvent *e);
+
+void mouse_grab_for_client(struct _ObClient *client, gboolean grab);
+
+ObFrameContext mouse_button_frame_context(ObFrameContext context,
+ guint button, guint state);
+
+/*! If a replay pointer is needed, then do it. Call this when windows are
+ going to be moving/appearing/disappearing, so that you know the mouse click
+ will go to the right window */
+void mouse_replay_pointer(void);
+
+#endif
diff --git a/openbox/moveresize.c b/openbox/moveresize.c
new file mode 100644
index 0000000..3a98db3
--- /dev/null
+++ b/openbox/moveresize.c
@@ -0,0 +1,1092 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ moveresize.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "grab.h"
+#include "framerender.h"
+#include "screen.h"
+#include "client.h"
+#include "focus.h"
+#include "frame.h"
+#include "openbox.h"
+#include "resist.h"
+#include "popup.h"
+#include "moveresize.h"
+#include "config.h"
+#include "event.h"
+#include "debug.h"
+#include "obrender/render.h"
+#include "obrender/theme.h"
+#include "obt/display.h"
+#include "obt/xqueue.h"
+#include "obt/prop.h"
+#include "obt/keyboard.h"
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+/* how far windows move and resize with the keyboard arrows */
+#define KEY_DIST 8
+#define SYNC_TIMEOUTS 4
+
+gboolean moveresize_in_progress = FALSE;
+ObClient *moveresize_client = NULL;
+#ifdef SYNC
+XSyncAlarm moveresize_alarm = None;
+#endif
+
+static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */
+
+/* starting geometry for the window being moved/resized, so it can be
+ restored */
+static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch;
+static gboolean was_max_horz, was_max_vert;
+static Rect pre_max_area;
+static gint cur_x, cur_y, cur_w, cur_h;
+static guint button;
+static guint32 corner;
+static ObDirection edge_warp_dir = -1;
+static gboolean edge_warp_odd = FALSE;
+static guint edge_warp_timer = 0;
+static ObDirection key_resize_edge = -1;
+static guint waiting_for_sync;
+#ifdef SYNC
+static guint sync_timer = 0;
+#endif
+
+static ObPopup *popup = NULL;
+
+static void do_move(gboolean keyboard, gint keydist);
+static void do_resize(void);
+static void do_edge_warp(gint x, gint y);
+static void cancel_edge_warp();
+#ifdef SYNC
+static gboolean sync_timeout_func(gpointer data);
+#endif
+
+static void client_dest(ObClient *client, gpointer data)
+{
+ if (moveresize_client == client)
+ moveresize_end(TRUE);
+}
+
+void moveresize_startup(gboolean reconfig)
+{
+ popup = popup_new();
+ popup_set_text_align(popup, RR_JUSTIFY_CENTER);
+
+ if (!reconfig)
+ client_add_destroy_notify(client_dest, NULL);
+}
+
+void moveresize_shutdown(gboolean reconfig)
+{
+ if (!reconfig) {
+ if (moveresize_in_progress)
+ moveresize_end(FALSE);
+ client_remove_destroy_notify(client_dest);
+ }
+
+ popup_free(popup);
+ popup = NULL;
+}
+
+static void popup_coords(ObClient *c, const gchar *format, gint a, gint b)
+{
+ gchar *text;
+
+ text = g_strdup_printf(format, a, b);
+ if (config_resize_popup_pos == OB_RESIZE_POS_TOP)
+ popup_position(popup, SouthGravity,
+ c->frame->area.x
+ + c->frame->area.width/2,
+ c->frame->area.y - ob_rr_theme->fbwidth);
+ else if (config_resize_popup_pos == OB_RESIZE_POS_CENTER)
+ popup_position(popup, CenterGravity,
+ c->frame->area.x + c->frame->area.width / 2,
+ c->frame->area.y + c->frame->area.height / 2);
+ else /* Fixed */ {
+ const Rect *area = screen_physical_area_active();
+ gint gravity, x, y;
+
+ x = config_resize_popup_fixed.x.pos;
+ if (config_resize_popup_fixed.x.center)
+ x = area->x + area->width/2;
+ else if (config_resize_popup_fixed.x.opposite)
+ x = RECT_RIGHT(*area) - x;
+ else
+ x = area->x + x;
+
+ y = config_resize_popup_fixed.y.pos;
+ if (config_resize_popup_fixed.y.center)
+ y = area->y + area->height/2;
+ else if (config_resize_popup_fixed.y.opposite)
+ y = RECT_RIGHT(*area) - y;
+ else
+ y = area->y + y;
+
+ if (config_resize_popup_fixed.x.center) {
+ if (config_resize_popup_fixed.y.center)
+ gravity = CenterGravity;
+ else if (config_resize_popup_fixed.y.opposite)
+ gravity = SouthGravity;
+ else
+ gravity = NorthGravity;
+ }
+ else if (config_resize_popup_fixed.x.opposite) {
+ if (config_resize_popup_fixed.y.center)
+ gravity = EastGravity;
+ else if (config_resize_popup_fixed.y.opposite)
+ gravity = SouthEastGravity;
+ else
+ gravity = NorthEastGravity;
+ }
+ else {
+ if (config_resize_popup_fixed.y.center)
+ gravity = WestGravity;
+ else if (config_resize_popup_fixed.y.opposite)
+ gravity = SouthWestGravity;
+ else
+ gravity = NorthWestGravity;
+ }
+
+ popup_position(popup, gravity, x, y);
+ }
+ popup_show(popup, text);
+ g_free(text);
+}
+
+void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr)
+{
+ ObCursor cur;
+ gboolean mv = (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) ||
+ cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD));
+ gint up = 1;
+ gint left = 1;
+
+ if (moveresize_in_progress || !c->frame->visible ||
+ !(mv ?
+ (c->functions & OB_CLIENT_FUNC_MOVE) :
+ (c->functions & OB_CLIENT_FUNC_RESIZE)))
+ return;
+
+ if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
+ cur = OB_CURSOR_NORTHWEST;
+ up = left = -1;
+ }
+ else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
+ cur = OB_CURSOR_NORTH;
+ up = -1;
+ }
+ else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
+ cur = OB_CURSOR_NORTHEAST;
+ up = -1;
+ }
+ else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT))
+ cur = OB_CURSOR_EAST;
+ else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT))
+ cur = OB_CURSOR_SOUTHEAST;
+ else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
+ cur = OB_CURSOR_SOUTH;
+ else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
+ cur = OB_CURSOR_SOUTHWEST;
+ left = -1;
+ }
+ else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
+ cur = OB_CURSOR_WEST;
+ left = -1;
+ }
+ else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD))
+ cur = OB_CURSOR_SOUTHEAST;
+ else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE))
+ cur = OB_CURSOR_MOVE;
+ else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
+ cur = OB_CURSOR_MOVE;
+ else
+ g_assert_not_reached();
+
+ /* keep the pointer bounded to the screen for move/resize */
+ if (!grab_pointer(FALSE, TRUE, cur))
+ return;
+ if (!grab_keyboard()) {
+ ungrab_pointer();
+ return;
+ }
+
+ frame_end_iconify_animation(c->frame);
+
+ moving = mv;
+ moveresize_client = c;
+ start_cx = c->area.x;
+ start_cy = c->area.y;
+ start_cw = c->area.width;
+ start_ch = c->area.height;
+ /* these adjustments for the size_inc make resizing a terminal more
+ friendly. you essentially start the resize in the middle of the
+ increment instead of at 0, so you have to move half an increment
+ either way instead of a full increment one and 1 px the other. */
+ start_x = x - (mv ? 0 : left * c->size_inc.width / 2);
+ start_y = y - (mv ? 0 : up * c->size_inc.height / 2);
+ corner = cnr;
+ button = b;
+ key_resize_edge = -1;
+
+ /* default to not putting max back on cancel */
+ was_max_horz = was_max_vert = FALSE;
+
+ /*
+ have to change start_cx and start_cy if going to do this..
+ if (corner == prop_atoms.net_wm_moveresize_move_keyboard ||
+ corner == prop_atoms.net_wm_moveresize_size_keyboard)
+ XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
+ c->area.width / 2, c->area.height / 2);
+ */
+
+ cur_x = start_cx;
+ cur_y = start_cy;
+ cur_w = start_cw;
+ cur_h = start_ch;
+
+ moveresize_in_progress = TRUE;
+ waiting_for_sync = 0;
+
+#ifdef SYNC
+ if (config_resize_redraw && !moving && obt_display_extension_sync &&
+ moveresize_client->sync_request && moveresize_client->sync_counter &&
+ !moveresize_client->not_responding)
+ {
+ /* Initialize values for the resize syncing, and create an alarm for
+ the client's xsync counter */
+
+ XSyncValue val;
+ XSyncAlarmAttributes aa;
+
+ /* set the counter to an initial value */
+ XSyncIntToValue(&val, 0);
+ XSyncSetCounter(obt_display, moveresize_client->sync_counter, val);
+
+ /* this will be incremented when we tell the client what we're
+ looking for */
+ moveresize_client->sync_counter_value = 0;
+
+ /* the next sequence we're waiting for with the alarm */
+ XSyncIntToValue(&val, 1);
+
+ /* set an alarm on the counter */
+ aa.trigger.counter = moveresize_client->sync_counter;
+ aa.trigger.wait_value = val;
+ aa.trigger.value_type = XSyncAbsolute;
+ aa.trigger.test_type = XSyncPositiveTransition;
+ aa.events = True;
+ XSyncIntToValue(&aa.delta, 1);
+ moveresize_alarm = XSyncCreateAlarm(obt_display,
+ XSyncCACounter |
+ XSyncCAValue |
+ XSyncCAValueType |
+ XSyncCATestType |
+ XSyncCADelta |
+ XSyncCAEvents,
+ &aa);
+ }
+#endif
+}
+
+void moveresize_end(gboolean cancel)
+{
+ ungrab_keyboard();
+ ungrab_pointer();
+
+ popup_hide(popup);
+
+ if (!moving) {
+#ifdef SYNC
+ /* turn off the alarm */
+ if (moveresize_alarm != None) {
+ XSyncDestroyAlarm(obt_display, moveresize_alarm);
+ moveresize_alarm = None;
+ }
+
+ if (sync_timer) g_source_remove(sync_timer);
+ sync_timer = 0;
+#endif
+ }
+
+ /* don't use client_move() here, use the same width/height as
+ we've been using during the move, otherwise we get different results
+ when moving maximized windows between monitors of different sizes !
+ */
+ client_configure(moveresize_client,
+ (cancel ? start_cx : cur_x),
+ (cancel ? start_cy : cur_y),
+ (cancel ? start_cw : cur_w),
+ (cancel ? start_ch : cur_h),
+ TRUE, TRUE, FALSE);
+
+ /* restore the client's maximized state. do this after putting the window
+ back in its original spot to minimize visible flicker */
+ if (cancel && (was_max_horz || was_max_vert)) {
+ const gboolean h = moveresize_client->max_horz;
+ const gboolean v = moveresize_client->max_vert;
+
+ client_maximize(moveresize_client, TRUE,
+ was_max_horz && was_max_vert ? 0 :
+ (was_max_horz ? 1 : 2));
+
+ /* replace the premax values with the ones we had saved if
+ the client doesn't have any already set */
+ if (was_max_horz && !h) {
+ moveresize_client->pre_max_area.x = pre_max_area.x;
+ moveresize_client->pre_max_area.width = pre_max_area.width;
+ }
+ if (was_max_vert && !v) {
+ moveresize_client->pre_max_area.y = pre_max_area.y;
+ moveresize_client->pre_max_area.height = pre_max_area.height;
+ }
+ }
+
+ /* dont edge warp after its ended */
+ cancel_edge_warp();
+
+ moveresize_in_progress = FALSE;
+ moveresize_client = NULL;
+}
+
+static void do_move(gboolean keyboard, gint keydist)
+{
+ gint resist;
+
+ if (keyboard) resist = keydist - 1; /* resist for one key press */
+ else resist = config_resist_win;
+ resist_move_windows(moveresize_client, resist, &cur_x, &cur_y);
+ if (!keyboard) resist = config_resist_edge;
+ resist_move_monitors(moveresize_client, resist, &cur_x, &cur_y);
+
+ client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
+ TRUE, FALSE, FALSE);
+ if (config_resize_popup_show == 2) /* == "Always" */
+ popup_coords(moveresize_client, "%d x %d",
+ moveresize_client->frame->area.x,
+ moveresize_client->frame->area.y);
+}
+
+static void do_resize(void)
+{
+ gint x, y, w, h, lw, lh;
+
+ /* see if it is actually going to resize
+ USE cur_x AND cur_y HERE ! Otherwise the try_configure won't know
+ what struts to use !!
+ */
+ x = cur_x;
+ y = cur_y;
+ w = cur_w;
+ h = cur_h;
+ client_try_configure(moveresize_client, &x, &y, &w, &h,
+ &lw, &lh, TRUE);
+ if (!(w == moveresize_client->area.width &&
+ h == moveresize_client->area.height) &&
+ /* if waiting_for_sync == 0, then we aren't waiting.
+ if it is > SYNC_TIMEOUTS, then we have timed out
+ that many times already, so forget about waiting more */
+ (waiting_for_sync == 0 || waiting_for_sync > SYNC_TIMEOUTS))
+ {
+#ifdef SYNC
+ if (config_resize_redraw && obt_display_extension_sync &&
+ /* don't send another sync when one is pending */
+ waiting_for_sync == 0 &&
+ moveresize_client->sync_request &&
+ moveresize_client->sync_counter &&
+ !moveresize_client->not_responding)
+ {
+ XEvent ce;
+ XSyncValue val;
+
+ /* increment the value we're waiting for */
+ ++moveresize_client->sync_counter_value;
+ XSyncIntToValue(&val, moveresize_client->sync_counter_value);
+
+ /* tell the client what we're waiting for */
+ ce.xclient.type = ClientMessage;
+ ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
+ ce.xclient.display = obt_display;
+ ce.xclient.window = moveresize_client->window;
+ ce.xclient.format = 32;
+ ce.xclient.data.l[0] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
+ ce.xclient.data.l[1] = event_time();
+ ce.xclient.data.l[2] = XSyncValueLow32(val);
+ ce.xclient.data.l[3] = XSyncValueHigh32(val);
+ ce.xclient.data.l[4] = 0l;
+ XSendEvent(obt_display, moveresize_client->window, FALSE,
+ NoEventMask, &ce);
+
+ waiting_for_sync = 1;
+
+ if (sync_timer) g_source_remove(sync_timer);
+ sync_timer = g_timeout_add(2000, sync_timeout_func, NULL);
+ }
+#endif
+
+ /* force a ConfigureNotify, it is part of the spec for SYNC resizing
+ and MUST follow the sync counter notification */
+ client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h,
+ TRUE, FALSE, TRUE);
+ }
+
+ /* this would be better with a fixed width font ... XXX can do it better
+ if there are 2 text boxes */
+ if (config_resize_popup_show == 2 || /* == "Always" */
+ (config_resize_popup_show == 1 && /* == "Nonpixel" */
+ moveresize_client->size_inc.width > 1 &&
+ moveresize_client->size_inc.height > 1))
+ popup_coords(moveresize_client, "%d x %d", lw, lh);
+}
+
+#ifdef SYNC
+static gboolean sync_timeout_func(gpointer data)
+{
+ ++waiting_for_sync; /* we timed out waiting for our sync... */
+ do_resize(); /* ...so let any pending resizes through */
+
+ if (waiting_for_sync > SYNC_TIMEOUTS) {
+ sync_timer = 0;
+ return FALSE; /* don't repeat */
+ }
+ else
+ return TRUE; /* keep waiting */
+}
+#endif
+
+static void calc_resize(gboolean keyboard, gint keydist, gint *dw, gint *dh,
+ ObDirection dir)
+{
+ gint resist, x = 0, y = 0, lw, lh, ow, oh, nw, nh;
+ gint trydw, trydh;
+
+ ow = cur_w;
+ oh = cur_h;
+ nw = ow + *dw;
+ nh = oh + *dh;
+
+ if (!keyboard &&
+ (moveresize_client->max_ratio || moveresize_client->min_ratio))
+ {
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ case OB_DIRECTION_SOUTH:
+ /* resize the width based on the height */
+ if (moveresize_client->min_ratio) {
+ if (nh * moveresize_client->min_ratio > nw)
+ nw = (gint)(nh * moveresize_client->min_ratio);
+ }
+ if (moveresize_client->max_ratio) {
+ if (nh * moveresize_client->max_ratio < nw)
+ nw = (gint)(nh * moveresize_client->max_ratio);
+ }
+ break;
+ default:
+ /* resize the height based on the width */
+ if (moveresize_client->min_ratio) {
+ if (nh * moveresize_client->min_ratio > nw)
+ nh = (gint)(nw / moveresize_client->min_ratio);
+ }
+ if (moveresize_client->max_ratio) {
+ if (nh * moveresize_client->max_ratio < nw)
+ nh = (gint)(nw / moveresize_client->max_ratio);
+ }
+ break;
+ }
+
+ /* see its actual size (apply aspect ratios) */
+ client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh,
+ TRUE);
+ trydw = nw - ow;
+ trydh = nh - oh;
+ }
+
+ /* resist_size_* needs the frame size */
+ nw += moveresize_client->frame->size.left +
+ moveresize_client->frame->size.right;
+ nh += moveresize_client->frame->size.top +
+ moveresize_client->frame->size.bottom;
+
+ if (keyboard) resist = keydist - 1; /* resist for one key press */
+ else resist = config_resist_win;
+ resist_size_windows(moveresize_client, resist, &nw, &nh, dir);
+ if (!keyboard) resist = config_resist_edge;
+ resist_size_monitors(moveresize_client, resist, &nw, &nh, dir);
+
+ nw -= moveresize_client->frame->size.left +
+ moveresize_client->frame->size.right;
+ nh -= moveresize_client->frame->size.top +
+ moveresize_client->frame->size.bottom;
+
+ *dw = nw - ow;
+ *dh = nh - oh;
+
+ /* take aspect ratios into account for resistance */
+ if (!keyboard &&
+ (moveresize_client->max_ratio || moveresize_client->min_ratio))
+ {
+ if (*dh != trydh) { /* got resisted */
+ /* resize the width based on the height */
+ if (moveresize_client->min_ratio) {
+ if (nh * moveresize_client->min_ratio > nw)
+ nw = (gint)(nh * moveresize_client->min_ratio);
+ }
+ if (moveresize_client->max_ratio) {
+ if (nh * moveresize_client->max_ratio < nw)
+ nw = (gint)(nh * moveresize_client->max_ratio);
+ }
+ }
+ if (*dw != trydw) { /* got resisted */
+ /* resize the height based on the width */
+ if (moveresize_client->min_ratio) {
+ if (nh * moveresize_client->min_ratio > nw)
+ nh = (gint)(nw / moveresize_client->min_ratio);
+ }
+ if (moveresize_client->max_ratio) {
+ if (nh * moveresize_client->max_ratio < nw)
+ nh = (gint)(nw / moveresize_client->max_ratio);
+ }
+ }
+ }
+
+ /* make sure it's all valid */
+ client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh, TRUE);
+
+ *dw = nw - ow;
+ *dh = nh - oh;
+}
+
+static void edge_warp_move_ptr(void)
+{
+ gint x, y;
+ const Rect* a;
+
+ screen_pointer_pos(&x, &y);
+ a = screen_physical_area_all_monitors();
+
+ switch (edge_warp_dir) {
+ case OB_DIRECTION_NORTH:
+ y = a->height - 1;
+ break;
+ case OB_DIRECTION_EAST:
+ x = a->x;
+ break;
+ case OB_DIRECTION_SOUTH:
+ y = a->y;
+ break;
+ case OB_DIRECTION_WEST:
+ x = a->width - 1;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ XWarpPointer(obt_display, 0, obt_root(ob_screen), 0, 0, 0, 0, x, y);
+}
+
+static gboolean edge_warp_delay_func(gpointer data)
+{
+ guint d;
+
+ /* only fire every second time. so it's fast the first time, but slower
+ after that */
+ if (edge_warp_odd) {
+ d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE);
+ if (d != screen_desktop) {
+ if (config_mouse_screenedgewarp) edge_warp_move_ptr();
+ screen_set_desktop(d, TRUE);
+ }
+ }
+ edge_warp_odd = !edge_warp_odd;
+
+ return TRUE; /* do repeat ! */
+}
+
+static void do_edge_warp(gint x, gint y)
+{
+ guint i;
+ ObDirection dir;
+
+ if (!config_mouse_screenedgetime) return;
+
+ dir = -1;
+
+ for (i = 0; i < screen_num_monitors; ++i) {
+ const Rect *a = screen_physical_area_monitor(i);
+ 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;
+ if (y == RECT_BOTTOM(*a)) dir = OB_DIRECTION_SOUTH;
+
+ /* try check for xinerama boundaries */
+ if ((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) &&
+ (dir == OB_DIRECTION_WEST || dir == OB_DIRECTION_EAST))
+ {
+ dir = -1;
+ }
+ if ((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) &&
+ (dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH))
+ {
+ dir = -1;
+ }
+ }
+
+ if (dir != edge_warp_dir) {
+ cancel_edge_warp();
+ if (dir != (ObDirection)-1) {
+ edge_warp_odd = TRUE; /* switch on the first timeout */
+ edge_warp_timer = g_timeout_add(config_mouse_screenedgetime,
+ edge_warp_delay_func, NULL);
+ }
+ edge_warp_dir = dir;
+ }
+}
+
+static void cancel_edge_warp(void)
+{
+ if (edge_warp_timer) g_source_remove(edge_warp_timer);
+ edge_warp_timer = 0;
+}
+
+static void move_with_keys(KeySym sym, guint state)
+{
+ gint dx = 0, dy = 0, ox = cur_x, oy = cur_y;
+ gint opx, px, opy, py;
+ gint dist = 0;
+
+ /* shift means jump to edge */
+ if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
+ {
+ gint x, y;
+ ObDirection dir;
+
+ if (sym == XK_Right)
+ dir = OB_DIRECTION_EAST;
+ else if (sym == XK_Left)
+ dir = OB_DIRECTION_WEST;
+ else if (sym == XK_Down)
+ dir = OB_DIRECTION_SOUTH;
+ else /* sym == XK_Up */
+ dir = OB_DIRECTION_NORTH;
+
+ client_find_move_directional(moveresize_client, dir, &x, &y);
+ dx = x - moveresize_client->area.x;
+ dy = y - moveresize_client->area.y;
+ } else {
+ /* control means fine grained */
+ if (state &
+ obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
+ {
+ dist = 1;
+ }
+ else
+ dist = KEY_DIST;
+
+ if (sym == XK_Right)
+ dx = dist;
+ else if (sym == XK_Left)
+ dx = -dist;
+ else if (sym == XK_Down)
+ dy = dist;
+ else /* if (sym == XK_Up) */
+ dy = -dist;
+ }
+
+ screen_pointer_pos(&opx, &opy);
+ XWarpPointer(obt_display, None, None, 0, 0, 0, 0, dx, dy);
+ /* steal the motion events this causes */
+ XSync(obt_display, FALSE);
+ {
+ XEvent ce;
+ while (xqueue_remove_local(&ce, xqueue_match_type,
+ GINT_TO_POINTER(MotionNotify)));
+ }
+ screen_pointer_pos(&px, &py);
+
+ cur_x += dx;
+ cur_y += dy;
+ do_move(TRUE, dist);
+
+ /* because the cursor moves even though the window does
+ not nessesarily (resistance), this adjusts where the curor
+ thinks it started so that it keeps up with where the window
+ actually is */
+ start_x += (px - opx) - (cur_x - ox);
+ start_y += (py - opy) - (cur_y - oy);
+}
+
+static void resize_with_keys(KeySym sym, guint state)
+{
+ gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py;
+ gint resist = 0;
+ ObDirection dir;
+
+ /* pick the edge if it needs to move */
+ if (sym == XK_Right) {
+ dir = OB_DIRECTION_EAST;
+ if (key_resize_edge != OB_DIRECTION_WEST &&
+ key_resize_edge != OB_DIRECTION_EAST)
+ {
+ key_resize_edge = OB_DIRECTION_EAST;
+ return;
+ }
+ } else if (sym == XK_Left) {
+ dir = OB_DIRECTION_WEST;
+ if (key_resize_edge != OB_DIRECTION_WEST &&
+ key_resize_edge != OB_DIRECTION_EAST)
+ {
+ key_resize_edge = OB_DIRECTION_WEST;
+ return;
+ }
+ } else if (sym == XK_Up) {
+ dir = OB_DIRECTION_NORTH;
+ if (key_resize_edge != OB_DIRECTION_NORTH &&
+ key_resize_edge != OB_DIRECTION_SOUTH)
+ {
+ key_resize_edge = OB_DIRECTION_NORTH;
+ return;
+ }
+ } else /* if (sym == XK_Down) */ {
+ dir = OB_DIRECTION_SOUTH;
+ if (key_resize_edge != OB_DIRECTION_NORTH &&
+ key_resize_edge != OB_DIRECTION_SOUTH)
+ {
+ key_resize_edge = OB_DIRECTION_SOUTH;
+ return;
+ }
+ }
+
+ /* shift means jump to edge */
+ if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT))
+ {
+ gint x, y, w, h;
+
+ if (sym == XK_Right)
+ dir = OB_DIRECTION_EAST;
+ else if (sym == XK_Left)
+ dir = OB_DIRECTION_WEST;
+ else if (sym == XK_Down)
+ dir = OB_DIRECTION_SOUTH;
+ else /* if (sym == XK_Up)) */
+ dir = OB_DIRECTION_NORTH;
+
+ client_find_resize_directional(moveresize_client, key_resize_edge,
+ key_resize_edge == dir,
+ &x, &y, &w, &h);
+ dw = w - moveresize_client->area.width;
+ dh = h - moveresize_client->area.height;
+ } else {
+ gint distw, disth;
+
+ /* control means fine grained */
+ if (moveresize_client->size_inc.width > 1) {
+ distw = moveresize_client->size_inc.width;
+ resist = 1;
+ }
+ else if (state &
+ obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
+ {
+ distw = 1;
+ resist = 1;
+ }
+ else {
+ distw = KEY_DIST;
+ resist = KEY_DIST;
+ }
+ if (moveresize_client->size_inc.height > 1) {
+ disth = moveresize_client->size_inc.height;
+ resist = 1;
+ }
+ else if (state &
+ obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL))
+ {
+ disth = 1;
+ resist = 1;
+ }
+ else {
+ disth = KEY_DIST;
+ resist = KEY_DIST;
+ }
+
+ if (key_resize_edge == OB_DIRECTION_WEST) {
+ if (dir == OB_DIRECTION_WEST)
+ dw = distw;
+ else
+ dw = -distw;
+ }
+ else if (key_resize_edge == OB_DIRECTION_EAST) {
+ if (dir == OB_DIRECTION_EAST)
+ dw = distw;
+ else
+ dw = -distw;
+ }
+ else if (key_resize_edge == OB_DIRECTION_NORTH) {
+ if (dir == OB_DIRECTION_NORTH)
+ dh = disth;
+ else
+ dh = -disth;
+ }
+ else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ {
+ if (dir == OB_DIRECTION_SOUTH)
+ dh = disth;
+ else
+ dh = -disth;
+ }
+ }
+
+ if (moveresize_client->max_horz &&
+ (key_resize_edge == OB_DIRECTION_WEST ||
+ key_resize_edge == OB_DIRECTION_EAST))
+ {
+ /* unmax horz */
+ was_max_horz = TRUE;
+ pre_max_area.x = moveresize_client->pre_max_area.x;
+ pre_max_area.width = moveresize_client->pre_max_area.width;
+
+ moveresize_client->pre_max_area.x = cur_x;
+ moveresize_client->pre_max_area.width = cur_w;
+ client_maximize(moveresize_client, FALSE, 1);
+ }
+ else if (moveresize_client->max_vert &&
+ (key_resize_edge == OB_DIRECTION_NORTH ||
+ key_resize_edge == OB_DIRECTION_SOUTH))
+ {
+ /* unmax vert */
+ was_max_vert = TRUE;
+ pre_max_area.y = moveresize_client->pre_max_area.y;
+ pre_max_area.height = moveresize_client->pre_max_area.height;
+
+ moveresize_client->pre_max_area.y = cur_y;
+ moveresize_client->pre_max_area.height = cur_h;
+ client_maximize(moveresize_client, FALSE, 2);
+ }
+
+ calc_resize(TRUE, resist, &dw, &dh, dir);
+ if (key_resize_edge == OB_DIRECTION_WEST)
+ cur_x -= dw;
+ else if (key_resize_edge == OB_DIRECTION_NORTH)
+ cur_y -= dh;
+ cur_w += dw;
+ cur_h += dh;
+
+ /* how to move the pointer to keep up with the change */
+ if (key_resize_edge == OB_DIRECTION_WEST)
+ pdx = -dw;
+ else if (key_resize_edge == OB_DIRECTION_EAST)
+ pdx = dw;
+ else if (key_resize_edge == OB_DIRECTION_NORTH)
+ pdy = -dh;
+ else if (key_resize_edge == OB_DIRECTION_SOUTH)
+ pdy = dh;
+
+ screen_pointer_pos(&opx, &opy);
+ XWarpPointer(obt_display, None, None, 0, 0, 0, 0, pdx, pdy);
+ /* steal the motion events this causes */
+ XSync(obt_display, FALSE);
+ {
+ XEvent ce;
+ while (xqueue_remove_local(&ce, xqueue_match_type,
+ GINT_TO_POINTER(MotionNotify)));
+ }
+ screen_pointer_pos(&px, &py);
+
+ do_resize();
+
+ /* because the cursor moves even though the window does
+ not nessesarily (resistance), this adjusts where the cursor
+ thinks it started so that it keeps up with where the window
+ actually is */
+ start_x += (px - opx) - dw;
+ start_y += (py - opy) - dh;
+
+}
+
+gboolean moveresize_event(XEvent *e)
+{
+ gboolean used = FALSE;
+
+ if (!moveresize_in_progress) return FALSE;
+
+ if (e->type == ButtonPress) {
+ if (!button) {
+ start_x = e->xbutton.x_root;
+ start_y = e->xbutton.y_root;
+ button = e->xbutton.button; /* this will end it now */
+ }
+ used = e->xbutton.button == button;
+ } else if (e->type == ButtonRelease) {
+ if (!button || e->xbutton.button == button) {
+ moveresize_end(FALSE);
+ used = TRUE;
+ }
+ } else if (e->type == MotionNotify) {
+ if (moving) {
+ cur_x = start_cx + e->xmotion.x_root - start_x;
+ cur_y = start_cy + e->xmotion.y_root - start_y;
+ do_move(FALSE, 0);
+ do_edge_warp(e->xmotion.x_root, e->xmotion.y_root);
+ } else {
+ gint dw, dh;
+ ObDirection dir;
+
+ if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) {
+ dw = -(e->xmotion.x_root - start_x);
+ dh = -(e->xmotion.y_root - start_y);
+ dir = OB_DIRECTION_NORTHWEST;
+ } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) {
+ dw = 0;
+ dh = -(e->xmotion.y_root - start_y);
+ dir = OB_DIRECTION_NORTH;
+ } else if (corner ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) {
+ dw = (e->xmotion.x_root - start_x);
+ dh = -(e->xmotion.y_root - start_y);
+ dir = OB_DIRECTION_NORTHEAST;
+ } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) {
+ dw = (e->xmotion.x_root - start_x);
+ dh = 0;
+ dir = OB_DIRECTION_EAST;
+ } else if (corner ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) {
+ dw = (e->xmotion.x_root - start_x);
+ dh = (e->xmotion.y_root - start_y);
+ dir = OB_DIRECTION_SOUTHEAST;
+ } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM))
+ {
+ dw = 0;
+ dh = (e->xmotion.y_root - start_y);
+ dir = OB_DIRECTION_SOUTH;
+ } else if (corner ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) {
+ dw = -(e->xmotion.x_root - start_x);
+ dh = (e->xmotion.y_root - start_y);
+ dir = OB_DIRECTION_SOUTHWEST;
+ } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) {
+ dw = -(e->xmotion.x_root - start_x);
+ dh = 0;
+ dir = OB_DIRECTION_WEST;
+ } else if (corner ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
+ dw = (e->xmotion.x_root - start_x);
+ dh = (e->xmotion.y_root - start_y);
+ dir = OB_DIRECTION_SOUTHEAST;
+ } else
+ g_assert_not_reached();
+
+ /* override the client's max state if desired */
+ if (ABS(dw) >= config_resist_edge) {
+ if (moveresize_client->max_horz) {
+ /* unmax horz */
+ was_max_horz = TRUE;
+ pre_max_area.x = moveresize_client->pre_max_area.x;
+ pre_max_area.width = moveresize_client->pre_max_area.width;
+
+ moveresize_client->pre_max_area.x = cur_x;
+ moveresize_client->pre_max_area.width = cur_w;
+ client_maximize(moveresize_client, FALSE, 1);
+ }
+ }
+ else if (was_max_horz && !moveresize_client->max_horz) {
+ /* remax horz and put the premax back */
+ client_maximize(moveresize_client, TRUE, 1);
+ moveresize_client->pre_max_area.x = pre_max_area.x;
+ moveresize_client->pre_max_area.width = pre_max_area.width;
+ }
+
+ if (ABS(dh) >= config_resist_edge) {
+ if (moveresize_client->max_vert) {
+ /* unmax vert */
+ was_max_vert = TRUE;
+ pre_max_area.y = moveresize_client->pre_max_area.y;
+ pre_max_area.height =
+ moveresize_client->pre_max_area.height;
+
+ moveresize_client->pre_max_area.y = cur_y;
+ moveresize_client->pre_max_area.height = cur_h;
+ client_maximize(moveresize_client, FALSE, 2);
+ }
+ }
+ else if (was_max_vert && !moveresize_client->max_vert) {
+ /* remax vert and put the premax back */
+ client_maximize(moveresize_client, TRUE, 2);
+ moveresize_client->pre_max_area.y = pre_max_area.y;
+ moveresize_client->pre_max_area.height = pre_max_area.height;
+ }
+
+ dw -= cur_w - start_cw;
+ dh -= cur_h - start_ch;
+
+ calc_resize(FALSE, 0, &dw, &dh, dir);
+ cur_w += dw;
+ cur_h += dh;
+
+ if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
+ corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
+ corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT))
+ {
+ cur_x -= dw;
+ }
+ if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
+ corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
+ corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT))
+ {
+ cur_y -= dh;
+ }
+
+ do_resize();
+ }
+ used = TRUE;
+ } else if (e->type == KeyPress) {
+ KeySym sym = obt_keyboard_keypress_to_keysym(e);
+
+ if (sym == XK_Escape) {
+ moveresize_end(TRUE);
+ used = TRUE;
+ } else if (sym == XK_Return || sym == XK_KP_Enter) {
+ moveresize_end(FALSE);
+ used = TRUE;
+ } else if (sym == XK_Right || sym == XK_Left ||
+ sym == XK_Up || sym == XK_Down)
+ {
+ if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) {
+ resize_with_keys(sym, e->xkey.state);
+ used = TRUE;
+ } else if (corner ==
+ OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
+ {
+ move_with_keys(sym, e->xkey.state);
+ used = TRUE;
+ }
+ }
+ }
+#ifdef SYNC
+ else if (e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
+ {
+ waiting_for_sync = 0; /* we got our sync... */
+ do_resize(); /* ...so try resize if there is more change pending */
+ used = TRUE;
+ }
+#endif
+
+ if (used && moveresize_client == focus_client)
+ event_update_user_time();
+
+ return used;
+}
diff --git a/openbox/moveresize.h b/openbox/moveresize.h
new file mode 100644
index 0000000..2d0f7dc
--- /dev/null
+++ b/openbox/moveresize.h
@@ -0,0 +1,52 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ moveresize.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __moveresize_h
+#define __moveresize_h
+
+#include <glib.h>
+
+#ifdef SYNC
+#include <X11/extensions/sync.h>
+#endif
+
+struct _ObClient;
+
+typedef enum {
+ OB_RESIZE_POS_CENTER,
+ OB_RESIZE_POS_TOP,
+ OB_RESIZE_POS_FIXED
+} ObResizePopupPos;
+
+extern gboolean moveresize_in_progress;
+extern struct _ObClient *moveresize_client;
+#ifdef SYNC
+extern XSyncAlarm moveresize_alarm;
+#endif
+
+void moveresize_startup(gboolean reconfig);
+void moveresize_shutdown(gboolean reconfig);
+
+/*! @param corner This is one of the prop_atoms.net_wm_moveresize_ atoms */
+void moveresize_start(struct _ObClient *c,
+ gint x, gint y, guint button, guint32 corner);
+void moveresize_end(gboolean cancel);
+
+gboolean moveresize_event(XEvent *e);
+
+#endif
diff --git a/openbox/mwm.h b/openbox/mwm.h
new file mode 100644
index 0000000..e7a9d39
--- /dev/null
+++ b/openbox/mwm.h
@@ -0,0 +1,78 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ mwm.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __ob__mwm_h
+#define __ob__mwm_h
+
+#include <glib.h>
+
+typedef struct _ObMwmHints ObMwmHints;
+
+/*! The MWM Hints as retrieved from the window property
+ This structure only contains 3 elements, even though the Motif 2.0
+ structure contains 5. We only use the first 3, so that is all gets
+ defined.
+*/
+struct _ObMwmHints
+{
+ /*! A bitmask of ObMwmFlags values */
+ guint flags;
+ /*! A bitmask of ObMwmFunctions values */
+ guint functions;
+ /*! A bitmask of ObMwmDecorations values */
+ guint decorations;
+};
+
+/*! The number of elements in the ObMwmHints struct */
+#define OB_MWM_ELEMENTS 3
+
+/*! Possible flags for MWM Hints (defined by Motif 2.0) */
+typedef enum
+{
+ OB_MWM_FLAG_FUNCTIONS = 1 << 0, /*!< The MMW Hints define funcs */
+ OB_MWM_FLAG_DECORATIONS = 1 << 1 /*!< The MWM Hints define decor */
+} ObMwmFlags;
+
+/*! Possible functions for MWM Hints (defined by Motif 2.0) */
+typedef enum
+{
+ OB_MWM_FUNC_ALL = 1 << 0, /*!< All functions */
+ OB_MWM_FUNC_RESIZE = 1 << 1, /*!< Allow resizing */
+ OB_MWM_FUNC_MOVE = 1 << 2, /*!< Allow moving */
+ OB_MWM_FUNC_ICONIFY = 1 << 3, /*!< Allow to be iconfied */
+ OB_MWM_FUNC_MAXIMIZE = 1 << 4 /*!< Allow to be maximized */
+#if 0
+ OM_MWM_FUNC_CLOSE = 1 << 5 /*!< Allow to be closed */
+#endif
+} ObMwmFunctions;
+
+/*! Possible decorations for MWM Hints (defined by Motif 2.0) */
+typedef enum
+{
+ OB_MWM_DECOR_ALL = 1 << 0, /*!< All decorations */
+ OB_MWM_DECOR_BORDER = 1 << 1, /*!< Show a border */
+ OB_MWM_DECOR_HANDLE = 1 << 2, /*!< Show a handle (bottom) */
+ OB_MWM_DECOR_TITLE = 1 << 3, /*!< Show a titlebar */
+#if 0
+ OB_MWM_DECOR_MENU = 1 << 4, /*!< Show a menu */
+#endif
+ OB_MWM_DECOR_ICONIFY = 1 << 5, /*!< Show an iconify button */
+ OB_MWM_DECOR_MAXIMIZE = 1 << 6 /*!< Show a maximize button */
+} ObMwmDecorations;
+
+#endif
diff --git a/openbox/openbox.c b/openbox/openbox.c
new file mode 100644
index 0000000..fbc01fd
--- /dev/null
+++ b/openbox/openbox.c
@@ -0,0 +1,779 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ openbox.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "debug.h"
+#include "openbox.h"
+#include "session.h"
+#include "dock.h"
+#include "event.h"
+#include "menu.h"
+#include "client.h"
+#include "screen.h"
+#include "actions.h"
+#include "startupnotify.h"
+#include "focus.h"
+#include "focus_cycle.h"
+#include "focus_cycle_indicator.h"
+#include "focus_cycle_popup.h"
+#include "moveresize.h"
+#include "frame.h"
+#include "framerender.h"
+#include "keyboard.h"
+#include "mouse.h"
+#include "menuframe.h"
+#include "grab.h"
+#include "group.h"
+#include "config.h"
+#include "ping.h"
+#include "prompt.h"
+#include "gettext.h"
+#include "obrender/render.h"
+#include "obrender/theme.h"
+#include "obt/display.h"
+#include "obt/xqueue.h"
+#include "obt/signal.h"
+#include "obt/prop.h"
+#include "obt/keyboard.h"
+#include "obt/xml.h"
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/types.h>
+# include <sys/wait.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <errno.h>
+
+#include <X11/cursorfont.h>
+#if USE_XCURSOR
+#include <X11/Xcursor/Xcursor.h>
+#endif
+
+RrInstance *ob_rr_inst;
+RrImageCache *ob_rr_icons;
+RrTheme *ob_rr_theme;
+GMainLoop *ob_main_loop;
+gint ob_screen;
+gboolean ob_replace_wm = FALSE;
+gboolean ob_sm_use = TRUE;
+gchar *ob_sm_id = NULL;
+gchar *ob_sm_save_file = NULL;
+gboolean ob_sm_restore = TRUE;
+gboolean ob_debug_xinerama = FALSE;
+const gchar *ob_locale_msg = NULL;
+
+static ObState state;
+static gboolean xsync = FALSE;
+static gboolean reconfigure = FALSE;
+static gboolean restart = FALSE;
+static gchar *restart_path = NULL;
+static Cursor cursors[OB_NUM_CURSORS];
+static gint exitcode = 0;
+static guint remote_control = 0;
+static gboolean being_replaced = FALSE;
+static gchar *config_file = NULL;
+static gchar *startup_cmd = NULL;
+
+static void signal_handler(gint signal, gpointer data);
+static void remove_args(gint *argc, gchar **argv, gint index, gint num);
+static void parse_env();
+static void parse_args(gint *argc, gchar **argv);
+static Cursor load_cursor(const gchar *name, guint fontval);
+static void run_startup_cmd(void);
+
+gint main(gint argc, gchar **argv)
+{
+ gchar *program_name;
+
+ obt_signal_listen();
+
+ ob_set_state(OB_STATE_STARTING);
+
+ ob_debug_startup();
+
+ /* initialize the locale */
+ if (!(ob_locale_msg = setlocale(LC_MESSAGES, "")))
+ g_message("Couldn't set messages locale category from environment.");
+ if (!setlocale(LC_ALL, ""))
+ g_message("Couldn't set locale from environment.");
+ bindtextdomain(PACKAGE_NAME, LOCALEDIR);
+ bind_textdomain_codeset(PACKAGE_NAME, "UTF-8");
+ textdomain(PACKAGE_NAME);
+
+ if (chdir(g_get_home_dir()) == -1)
+ g_message(_("Unable to change to home directory \"%s\": %s"),
+ g_get_home_dir(), g_strerror(errno));
+
+ /* parse the command line args, which can change the argv[0] */
+ parse_args(&argc, argv);
+ /* parse the environment variables */
+ parse_env();
+
+ program_name = g_path_get_basename(argv[0]);
+ g_set_prgname(program_name);
+
+ if (!remote_control)
+ session_startup(argc, argv);
+
+ if (!obt_display_open(NULL))
+ ob_exit_with_error(_("Failed to open the display from the DISPLAY environment variable."));
+
+ if (remote_control) {
+ /* Send client message telling the OB process to:
+ * remote_control = 1 -> reconfigure
+ * remote_control = 2 -> restart */
+ OBT_PROP_MSG(ob_screen, obt_root(ob_screen),
+ OB_CONTROL, remote_control, 0, 0, 0, 0);
+ obt_display_close();
+ exit(EXIT_SUCCESS);
+ }
+
+ ob_main_loop = g_main_loop_new(NULL, FALSE);
+
+ /* set up signal handlers, they are called from the mainloop
+ in the main program's thread */
+ obt_signal_add_callback(SIGUSR1, signal_handler, NULL);
+ obt_signal_add_callback(SIGUSR2, signal_handler, NULL);
+ obt_signal_add_callback(SIGTERM, signal_handler, NULL);
+ obt_signal_add_callback(SIGINT, signal_handler, NULL);
+ obt_signal_add_callback(SIGHUP, signal_handler, NULL);
+ obt_signal_add_callback(SIGPIPE, signal_handler, NULL);
+ obt_signal_add_callback(SIGCHLD, signal_handler, NULL);
+ obt_signal_add_callback(SIGTTIN, signal_handler, NULL);
+ obt_signal_add_callback(SIGTTOU, signal_handler, NULL);
+
+ ob_screen = DefaultScreen(obt_display);
+
+ ob_rr_inst = RrInstanceNew(obt_display, ob_screen);
+ if (ob_rr_inst == NULL)
+ ob_exit_with_error(_("Failed to initialize the obrender library."));
+ /* Saving 3 resizes of an RrImage makes a lot of sense for icons, as there
+ are generally 3 icon sizes needed: the titlebar icon, the menu icon,
+ and the alt-tab icon
+ */
+ ob_rr_icons = RrImageCacheNew(3);
+
+ XSynchronize(obt_display, xsync);
+
+ /* check for locale support */
+ if (!XSupportsLocale())
+ g_message(_("X server does not support locale."));
+ if (!XSetLocaleModifiers(""))
+ g_message(_("Cannot set locale modifiers for the X server."));
+
+ /* set the DISPLAY environment variable for any lauched children, to the
+ display we're using, so they open in the right place. */
+ g_setenv("DISPLAY", DisplayString(obt_display), TRUE);
+
+ /* create available cursors */
+ cursors[OB_CURSOR_NONE] = None;
+ cursors[OB_CURSOR_POINTER] = load_cursor("left_ptr", XC_left_ptr);
+ cursors[OB_CURSOR_BUSYPOINTER] = load_cursor("left_ptr_watch",XC_left_ptr);
+ cursors[OB_CURSOR_BUSY] = load_cursor("watch", XC_watch);
+ cursors[OB_CURSOR_MOVE] = load_cursor("fleur", XC_fleur);
+ cursors[OB_CURSOR_NORTH] = load_cursor("top_side", XC_top_side);
+ cursors[OB_CURSOR_NORTHEAST] = load_cursor("top_right_corner",
+ XC_top_right_corner);
+ cursors[OB_CURSOR_EAST] = load_cursor("right_side", XC_right_side);
+ cursors[OB_CURSOR_SOUTHEAST] = load_cursor("bottom_right_corner",
+ XC_bottom_right_corner);
+ cursors[OB_CURSOR_SOUTH] = load_cursor("bottom_side", XC_bottom_side);
+ cursors[OB_CURSOR_SOUTHWEST] = load_cursor("bottom_left_corner",
+ XC_bottom_left_corner);
+ cursors[OB_CURSOR_WEST] = load_cursor("left_side", XC_left_side);
+ cursors[OB_CURSOR_NORTHWEST] = load_cursor("top_left_corner",
+ XC_top_left_corner);
+
+ if (screen_annex()) { /* it will be ours! */
+
+ /* get a timestamp from after taking over as the WM. if we use the
+ old timestamp to set focus it can fail when replacing another WM. */
+ event_reset_time();
+
+ do {
+ ObPrompt *xmlprompt = NULL;
+
+ if (reconfigure) obt_keyboard_reload();
+
+ {
+ ObtXmlInst *i;
+
+ /* startup the parsing so everything can register sections
+ of the rc */
+ i = obt_xml_instance_new();
+
+ /* register all the available actions */
+ actions_startup(reconfigure);
+ /* start up config which sets up with the parser */
+ config_startup(i);
+
+ /* parse/load user options */
+ if ((config_file &&
+ obt_xml_load_file(i, config_file, "openbox_config")) ||
+ obt_xml_load_config_file(i, "openbox", "rc.xml",
+ "openbox_config"))
+ {
+ obt_xml_tree_from_root(i);
+ obt_xml_close(i);
+ }
+ else {
+ g_message(_("Unable to find a valid config file, using some simple defaults"));
+ config_file = NULL;
+ }
+
+ if (config_file) {
+ gchar *p = g_filename_to_utf8(config_file, -1,
+ NULL, NULL, NULL);
+ if (p)
+ OBT_PROP_SETS(obt_root(ob_screen), OB_CONFIG_FILE, p);
+ g_free(p);
+ }
+ else
+ OBT_PROP_ERASE(obt_root(ob_screen), OB_CONFIG_FILE);
+
+ /* we're done with parsing now, kill it */
+ obt_xml_instance_unref(i);
+ }
+
+ /* load the theme specified in the rc file */
+ {
+ RrTheme *theme;
+ if ((theme = RrThemeNew(ob_rr_inst, config_theme, TRUE,
+ config_font_activewindow,
+ config_font_inactivewindow,
+ config_font_menutitle,
+ config_font_menuitem,
+ config_font_activeosd,
+ config_font_inactiveosd)))
+ {
+ RrThemeFree(ob_rr_theme);
+ ob_rr_theme = theme;
+ }
+ if (ob_rr_theme == NULL)
+ ob_exit_with_error(_("Unable to load a theme."));
+
+ OBT_PROP_SETS(obt_root(ob_screen), OB_THEME,
+ ob_rr_theme->name);
+ }
+
+ if (reconfigure) {
+ GList *it;
+
+ /* update all existing windows for the new theme */
+ for (it = client_list; it; it = g_list_next(it)) {
+ ObClient *c = it->data;
+ frame_adjust_theme(c->frame);
+ }
+ }
+ event_startup(reconfigure);
+ /* focus_backup is used for stacking, so this needs to come before
+ anything that calls stacking_add */
+ sn_startup(reconfigure);
+ window_startup(reconfigure);
+ focus_startup(reconfigure);
+ focus_cycle_startup(reconfigure);
+ focus_cycle_indicator_startup(reconfigure);
+ focus_cycle_popup_startup(reconfigure);
+ screen_startup(reconfigure);
+ grab_startup(reconfigure);
+ group_startup(reconfigure);
+ ping_startup(reconfigure);
+ client_startup(reconfigure);
+ dock_startup(reconfigure);
+ moveresize_startup(reconfigure);
+ keyboard_startup(reconfigure);
+ mouse_startup(reconfigure);
+ menu_frame_startup(reconfigure);
+ menu_startup(reconfigure);
+ prompt_startup(reconfigure);
+
+ /* do this after everything is started so no events will get
+ missed */
+ xqueue_listen();
+
+ if (!reconfigure) {
+ guint32 xid;
+ ObWindow *w;
+
+ /* get all the existing windows */
+ window_manage_all();
+
+ /* focus what was focused if a wm was already running */
+ if (OBT_PROP_GET32(obt_root(ob_screen),
+ NET_ACTIVE_WINDOW, WINDOW, &xid) &&
+ (w = window_find(xid)) && WINDOW_IS_CLIENT(w))
+ {
+ client_focus(WINDOW_AS_CLIENT(w));
+ }
+ } else {
+ GList *it;
+
+ /* redecorate all existing windows */
+ for (it = client_list; it; it = g_list_next(it)) {
+ ObClient *c = it->data;
+
+ /* the new config can change the window's decorations */
+ client_setup_decor_and_functions(c, FALSE);
+ /* redraw the frames */
+ frame_adjust_area(c->frame, TRUE, TRUE, FALSE);
+ /* the decor sizes may have changed, so the windows may
+ end up in new positions */
+ client_reconfigure(c, FALSE);
+ }
+ }
+
+ ob_set_state(OB_STATE_RUNNING);
+
+ if (!reconfigure && startup_cmd) run_startup_cmd();
+
+ reconfigure = FALSE;
+
+ /* look for parsing errors */
+ {
+ xmlErrorPtr e = xmlGetLastError();
+ if (e) {
+ gchar *m;
+
+ m = g_strdup_printf(_("One or more XML syntax errors were found while parsing the Openbox configuration files. See stdout for more information. The last error seen was in file \"%s\" line %d, with message: %s"), e->file, e->line, e->message);
+ xmlprompt =
+ prompt_show_message(m, _("Openbox Syntax Error"), _("Close"));
+ g_free(m);
+ xmlResetError(e);
+ }
+ }
+
+ g_main_loop_run(ob_main_loop);
+ ob_set_state(reconfigure ?
+ OB_STATE_RECONFIGURING : OB_STATE_EXITING);
+
+ if (xmlprompt) {
+ prompt_unref(xmlprompt);
+ xmlprompt = NULL;
+ }
+
+ if (!reconfigure)
+ window_unmanage_all();
+
+ prompt_shutdown(reconfigure);
+ menu_shutdown(reconfigure);
+ menu_frame_shutdown(reconfigure);
+ mouse_shutdown(reconfigure);
+ keyboard_shutdown(reconfigure);
+ moveresize_shutdown(reconfigure);
+ dock_shutdown(reconfigure);
+ client_shutdown(reconfigure);
+ ping_shutdown(reconfigure);
+ group_shutdown(reconfigure);
+ grab_shutdown(reconfigure);
+ screen_shutdown(reconfigure);
+ focus_cycle_popup_shutdown(reconfigure);
+ focus_cycle_indicator_shutdown(reconfigure);
+ focus_cycle_shutdown(reconfigure);
+ focus_shutdown(reconfigure);
+ window_shutdown(reconfigure);
+ sn_shutdown(reconfigure);
+ event_shutdown(reconfigure);
+ config_shutdown();
+ actions_shutdown(reconfigure);
+ } while (reconfigure);
+ }
+
+ XSync(obt_display, FALSE);
+
+ RrThemeFree(ob_rr_theme);
+ RrImageCacheUnref(ob_rr_icons);
+ RrInstanceFree(ob_rr_inst);
+
+ session_shutdown(being_replaced);
+
+ obt_display_close();
+
+ if (restart) {
+ ob_debug_shutdown();
+ obt_signal_stop();
+ if (restart_path != NULL) {
+ gint argcp;
+ gchar **argvp;
+ GError *err = NULL;
+
+ /* run other window manager */
+ if (g_shell_parse_argv(restart_path, &argcp, &argvp, &err)) {
+ execvp(argvp[0], argvp);
+ g_strfreev(argvp);
+ } else {
+ g_message(
+ _("Restart failed to execute new executable \"%s\": %s"),
+ restart_path, err->message);
+ g_error_free(err);
+ }
+ }
+
+ /* we remove the session arguments from argv, so put them back,
+ also don't restore the session on restart */
+ if (ob_sm_save_file != NULL || ob_sm_id != NULL) {
+ gchar **nargv;
+ gint i, l;
+
+ l = argc + 1 +
+ (ob_sm_save_file != NULL ? 2 : 0) +
+ (ob_sm_id != NULL ? 2 : 0);
+ nargv = g_new0(gchar*, l+1);
+ for (i = 0; i < argc; ++i)
+ nargv[i] = argv[i];
+
+ if (ob_sm_save_file != NULL) {
+ nargv[i++] = g_strdup("--sm-save-file");
+ nargv[i++] = ob_sm_save_file;
+ }
+ if (ob_sm_id != NULL) {
+ nargv[i++] = g_strdup("--sm-client-id");
+ nargv[i++] = ob_sm_id;
+ }
+ nargv[i++] = g_strdup("--sm-no-load");
+ g_assert(i == l);
+ argv = nargv;
+ }
+
+ /* re-run me */
+ execvp(argv[0], argv); /* try how we were run */
+ execlp(argv[0], program_name, (gchar*)NULL); /* last resort */
+ }
+
+ /* free stuff passed in from the command line or environment */
+ g_free(ob_sm_save_file);
+ g_free(ob_sm_id);
+ g_free(program_name);
+
+ if (!restart) {
+ ob_debug_shutdown();
+ obt_signal_stop();
+ }
+
+ return exitcode;
+}
+
+static void signal_handler(gint signal, gpointer data)
+{
+ switch (signal) {
+ case SIGUSR1:
+ ob_debug("Caught signal %d. Restarting.", signal);
+ ob_restart();
+ break;
+ case SIGUSR2:
+ ob_debug("Caught signal %d. Reconfiguring.", signal);
+ ob_reconfigure();
+ break;
+ case SIGCHLD:
+ /* reap children */
+ while (waitpid(-1, NULL, WNOHANG) > 0);
+ break;
+ case SIGTTIN:
+ case SIGTTOU:
+ ob_debug("Caught signal %d. Ignoring.", signal);
+ break;
+ default:
+ ob_debug("Caught signal %d. Exiting.", signal);
+ /* TERM and INT return a 0 code */
+ ob_exit(!(signal == SIGTERM || signal == SIGINT));
+ }
+}
+
+static void print_version(void)
+{
+ g_print("Openbox %s\n", PACKAGE_VERSION);
+ g_print(_("Copyright (c)"));
+ g_print(" 2008-2011 Mikael Magnusson\n");
+ g_print(_("Copyright (c)"));
+ g_print(" 2003-2011 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");
+}
+
+static void print_help(void)
+{
+ g_print(_("Syntax: openbox [options]\n"));
+ g_print(_("\nOptions:\n"));
+ g_print(_(" --help Display this help and exit\n"));
+ g_print(_(" --version Display the version and exit\n"));
+ g_print(_(" --replace Replace the currently running window manager\n"));
+ /* TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+ aligned still, if you have to, make a new line with \n and 22 spaces. It's
+ fine to leave it as FILE though. */
+ g_print(_(" --config-file FILE Specify the path to the config file to use\n"));
+ g_print(_(" --sm-disable Disable connection to the session manager\n"));
+ g_print(_("\nPassing messages to a running Openbox instance:\n"));
+ g_print(_(" --reconfigure Reload Openbox's configuration\n"));
+ g_print(_(" --restart Restart Openbox\n"));
+ g_print(_(" --exit Exit Openbox\n"));
+ g_print(_("\nDebugging options:\n"));
+ g_print(_(" --sync Run in synchronous mode\n"));
+ g_print(_(" --startup CMD Run CMD after starting\n"));
+ g_print(_(" --debug Display debugging output\n"));
+ g_print(_(" --debug-focus Display debugging output for focus handling\n"));
+ g_print(_(" --debug-session Display debugging output for session management\n"));
+ g_print(_(" --debug-xinerama Split the display into fake xinerama screens\n"));
+ g_print(_("\nPlease report bugs at %s\n"), PACKAGE_BUGREPORT);
+}
+
+static void remove_args(gint *argc, gchar **argv, gint index, gint num)
+{
+ gint i;
+
+ for (i = index; i < *argc - num; ++i)
+ argv[i] = argv[i+num];
+ for (; i < *argc; ++i)
+ argv[i] = NULL;
+ *argc -= num;
+}
+
+static void run_startup_cmd(void)
+{
+ gchar **argv = NULL;
+ GError *e = NULL;
+ gboolean ok;
+
+ if (!g_shell_parse_argv(startup_cmd, NULL, &argv, &e)) {
+ g_message("Error parsing startup command: %s",
+ e->message);
+ g_error_free(e);
+ e = NULL;
+ }
+ ok = g_spawn_async(NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH |
+ G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL, NULL, NULL, &e);
+ if (!ok) {
+ g_message("Error launching startup command: %s",
+ e->message);
+ g_error_free(e);
+ e = NULL;
+ }
+}
+
+static void parse_env(void)
+{
+ const gchar *id;
+
+ /* unset this so we don't pass it on unknowingly */
+ g_unsetenv("DESKTOP_STARTUP_ID");
+
+ /* this is how gnome-session passes in a session client id */
+ id = g_getenv("DESKTOP_AUTOSTART_ID");
+ if (id) {
+ g_unsetenv("DESKTOP_AUTOSTART_ID");
+ if (ob_sm_id) g_free(ob_sm_id);
+ ob_sm_id = g_strdup(id);
+ ob_debug_type(OB_DEBUG_SM,
+ "DESKTOP_AUTOSTART_ID %s supercedes --sm-client-id\n",
+ ob_sm_id);
+ }
+}
+
+static void parse_args(gint *argc, gchar **argv)
+{
+ gint i;
+
+ for (i = 1; i < *argc; ++i) {
+ if (!strcmp(argv[i], "--version")) {
+ print_version();
+ exit(0);
+ }
+ else if (!strcmp(argv[i], "--help")) {
+ print_help();
+ exit(0);
+ }
+ else if (!strcmp(argv[i], "--g-fatal-warnings")) {
+ g_log_set_always_fatal(G_LOG_LEVEL_CRITICAL);
+ }
+ else if (!strcmp(argv[i], "--replace")) {
+ ob_replace_wm = TRUE;
+ remove_args(argc, argv, i, 1);
+ --i; /* this arg was removed so go back */
+ }
+ else if (!strcmp(argv[i], "--sync")) {
+ xsync = TRUE;
+ }
+ else if (!strcmp(argv[i], "--startup")) {
+ if (i == *argc - 1) /* no args left */
+ g_printerr(_("%s requires an argument\n"), "--startup");
+ else {
+ /* this will be in the current locale encoding, which is
+ what we want */
+ startup_cmd = argv[i+1];
+ remove_args(argc, argv, i, 2);
+ --i; /* this arg was removed so go back */
+ ob_debug("--startup %s", startup_cmd);
+ }
+ }
+ else if (!strcmp(argv[i], "--debug")) {
+ ob_debug_enable(OB_DEBUG_NORMAL, TRUE);
+ ob_debug_enable(OB_DEBUG_APP_BUGS, TRUE);
+ }
+ else if (!strcmp(argv[i], "--debug-focus")) {
+ ob_debug_enable(OB_DEBUG_FOCUS, TRUE);
+ }
+ else if (!strcmp(argv[i], "--debug-session")) {
+ ob_debug_enable(OB_DEBUG_SM, TRUE);
+ }
+ else if (!strcmp(argv[i], "--debug-xinerama")) {
+ ob_debug_xinerama = TRUE;
+ }
+ else if (!strcmp(argv[i], "--reconfigure")) {
+ remote_control = 1;
+ }
+ else if (!strcmp(argv[i], "--restart")) {
+ remote_control = 2;
+ }
+ else if (!strcmp(argv[i], "--exit")) {
+ remote_control = 3;
+ }
+ else if (!strcmp(argv[i], "--config-file")) {
+ if (i == *argc - 1) /* no args left */
+ g_printerr(_("%s requires an argument\n"), "--config-file");
+ else {
+ /* this will be in the current locale encoding, which is
+ what we want */
+ config_file = argv[i+1];
+ ++i; /* skip the argument */
+ ob_debug("--config-file %s", config_file);
+ }
+ }
+ else if (!strcmp(argv[i], "--sm-save-file")) {
+ if (i == *argc - 1) /* no args left */
+ /* not translated cuz it's sekret */
+ g_printerr("--sm-save-file requires an argument\n");
+ else {
+ ob_sm_save_file = g_strdup(argv[i+1]);
+ remove_args(argc, argv, i, 2);
+ --i; /* this arg was removed so go back */
+ ob_debug_type(OB_DEBUG_SM, "--sm-save-file %s",
+ ob_sm_save_file);
+ }
+ }
+ else if (!strcmp(argv[i], "--sm-client-id")) {
+ if (i == *argc - 1) /* no args left */
+ /* not translated cuz it's sekret */
+ g_printerr("--sm-client-id requires an argument\n");
+ else {
+ ob_sm_id = g_strdup(argv[i+1]);
+ remove_args(argc, argv, i, 2);
+ --i; /* this arg was removed so go back */
+ ob_debug_type(OB_DEBUG_SM, "--sm-client-id %s", ob_sm_id);
+ }
+ }
+ else if (!strcmp(argv[i], "--sm-disable")) {
+ ob_sm_use = FALSE;
+ }
+ else if (!strcmp(argv[i], "--sm-no-load")) {
+ ob_sm_restore = FALSE;
+ remove_args(argc, argv, i, 1);
+ --i; /* this arg was removed so go back */
+ }
+ else {
+ /* this is a memleak.. oh well.. heh */
+ gchar *err = g_strdup_printf
+ (_("Invalid command line argument \"%s\"\n"), argv[i]);
+ ob_exit_with_error(err);
+ }
+ }
+}
+
+static Cursor load_cursor(const gchar *name, guint fontval)
+{
+ Cursor c = None;
+
+#if USE_XCURSOR
+ c = XcursorLibraryLoadCursor(obt_display, name);
+#endif
+ if (c == None)
+ c = XCreateFontCursor(obt_display, fontval);
+ return c;
+}
+
+void ob_exit_with_error(const gchar *msg)
+{
+ g_message("%s", msg);
+ session_shutdown(TRUE);
+ exit(EXIT_FAILURE);
+}
+
+void ob_restart_other(const gchar *path)
+{
+ restart_path = g_strdup(path);
+ ob_restart();
+}
+
+void ob_restart(void)
+{
+ restart = TRUE;
+ ob_exit(0);
+}
+
+void ob_reconfigure(void)
+{
+ reconfigure = TRUE;
+ ob_exit(0);
+}
+
+void ob_exit(gint code)
+{
+ exitcode = code;
+ g_main_loop_quit(ob_main_loop);
+}
+
+void ob_exit_replace(void)
+{
+ exitcode = 0;
+ being_replaced = TRUE;
+ g_main_loop_quit(ob_main_loop);
+}
+
+Cursor ob_cursor(ObCursor cursor)
+{
+ g_assert(cursor < OB_NUM_CURSORS);
+ return cursors[cursor];
+}
+
+ObState ob_state(void)
+{
+ return state;
+}
+
+void ob_set_state(ObState s)
+{
+ state = s;
+}
diff --git a/openbox/openbox.h b/openbox/openbox.h
new file mode 100644
index 0000000..94f4910
--- /dev/null
+++ b/openbox/openbox.h
@@ -0,0 +1,66 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ openbox.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __openbox_h
+#define __openbox_h
+
+#include "misc.h"
+
+#include "obrender/render.h"
+#include "obrender/theme.h"
+#include "obt/display.h"
+
+#include <glib.h>
+
+extern RrInstance *ob_rr_inst;
+extern RrImageCache *ob_rr_icons;
+extern RrTheme *ob_rr_theme;
+
+extern GMainLoop *ob_main_loop;
+
+/*! The number of the screen on which we're running */
+extern gint ob_screen;
+
+extern gboolean ob_sm_use;
+extern gchar *ob_sm_id;
+/* This save_file will get pass to ourselves if we restart too! So we won't
+ make a new file every time, yay. */
+extern gchar *ob_sm_save_file;
+extern gboolean ob_sm_restore;
+extern gboolean ob_replace_wm;
+extern gboolean ob_debug_xinerama;
+
+/*! The current locale for the LC_MESSAGES category */
+extern const gchar *ob_locale_msg;
+
+/* The state of execution of the window manager */
+ObState ob_state(void);
+void ob_set_state(ObState state);
+
+void ob_restart_other(const gchar *path);
+void ob_restart(void);
+void ob_exit(gint code);
+void ob_exit_replace(void);
+
+void ob_reconfigure(void);
+
+void ob_exit_with_error(const gchar *msg) G_GNUC_NORETURN;
+
+Cursor ob_cursor(ObCursor cursor);
+
+#endif
diff --git a/openbox/ping.c b/openbox/ping.c
new file mode 100644
index 0000000..7cb4766
--- /dev/null
+++ b/openbox/ping.c
@@ -0,0 +1,165 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ client.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2008 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "ping.h"
+#include "client.h"
+#include "event.h"
+#include "debug.h"
+#include "openbox.h"
+#include "obt/prop.h"
+
+typedef struct _ObPingTarget
+{
+ ObClient *client;
+ ObPingEventHandler h;
+ guint32 id;
+ guint loopid;
+ gint waiting;
+} ObPingTarget;
+
+static GHashTable *ping_ids = NULL;
+static guint32 ping_next_id = 1;
+
+#define PING_TIMEOUT 3000 /* in MS */
+/*! Warn the user after this many PING_TIMEOUT intervals */
+#define PING_TIMEOUT_WARN 2
+
+static void ping_send(ObPingTarget *t);
+static void ping_end(ObClient *client, gpointer data);
+static gboolean ping_timeout(gpointer data);
+static gboolean find_client(gpointer key, gpointer value, gpointer client);
+
+void ping_startup(gboolean reconfigure)
+{
+ if (reconfigure) return;
+
+ ping_ids = g_hash_table_new(g_int_hash, g_int_equal);
+
+ /* listen for clients to disappear */
+ client_add_destroy_notify(ping_end, NULL);
+}
+
+void ping_shutdown(gboolean reconfigure)
+{
+ if (reconfigure) return;
+
+ g_hash_table_unref(ping_ids);
+ ping_ids = NULL;
+
+ client_remove_destroy_notify(ping_end);
+}
+
+void ping_start(struct _ObClient *client, ObPingEventHandler h)
+{
+ ObPingTarget *t;
+
+ /* make sure the client supports ping! */
+ g_assert(client->ping == TRUE);
+
+ /* make sure we're not already pinging the client */
+ if (g_hash_table_find(ping_ids, find_client, client) != NULL) return;
+
+ t = g_slice_new0(ObPingTarget);
+ t->client = client;
+ t->h = h;
+
+ t->loopid = g_timeout_add_full(G_PRIORITY_DEFAULT, PING_TIMEOUT,
+ ping_timeout, t, NULL);
+ /* act like we just timed out immediately, to start the pinging process
+ now instead of after the first delay. this makes sure the client
+ ends up in the ping_ids hash table now. */
+ ping_timeout(t);
+
+ /* make sure we can remove the client later */
+ g_assert(g_hash_table_find(ping_ids, find_client, client) != NULL);
+}
+
+void ping_got_pong(guint32 id)
+{
+ ObPingTarget *t;
+
+ if ((t = g_hash_table_lookup(ping_ids, &id))) {
+ /*ob_debug("-PONG: '%s' (id %u)", t->client->title, t->id);*/
+ if (t->waiting > PING_TIMEOUT_WARN) {
+ /* we had notified that they weren't responding, so now we
+ need to notify that they are again */
+ t->h(t->client, FALSE);
+ }
+ t->waiting = 0; /* not waiting for a reply anymore */
+
+ /* we got a pong so we're happy now */
+ ping_end(t->client, NULL);
+ }
+ else
+ ob_debug("Got PONG with id %u but not waiting for one", id);
+}
+
+static gboolean find_client(gpointer key, gpointer value, gpointer client)
+{
+ ObPingTarget *t = value;
+ return t->client == client;
+}
+
+static void ping_send(ObPingTarget *t)
+{
+ /* t->id is 0 when it hasn't been assigned an id ever yet.
+ we can reuse ids when t->waiting == 0, because we won't be getting a
+ pong for that id in the future again. that way for apps that aren't
+ timing out we don't need to remove/add them from/to the hash table */
+ if (t->id == 0 || t->waiting > 0) {
+ /* pick an id, and reinsert in the hash table with the new id */
+ if (t->id) g_hash_table_remove(ping_ids, &t->id);
+ t->id = ping_next_id;
+ if (++ping_next_id == 0) ++ping_next_id; /* skip 0 on wraparound */
+ g_hash_table_insert(ping_ids, &t->id, t);
+ }
+
+ /*ob_debug("+PING: '%s' (id %u)", t->client->title, t->id);*/
+ OBT_PROP_MSG_TO(t->client->window, t->client->window, WM_PROTOCOLS,
+ OBT_PROP_ATOM(NET_WM_PING), t->id, t->client->window, 0, 0,
+ NoEventMask);
+}
+
+static gboolean ping_timeout(gpointer data)
+{
+ ObPingTarget *t = data;
+
+ ping_send(t);
+
+ /* if the client hasn't been responding then do something about it */
+ if (t->waiting == PING_TIMEOUT_WARN)
+ t->h(t->client, TRUE); /* notify that the client isn't responding */
+
+ ++t->waiting;
+
+ return TRUE; /* repeat */
+}
+
+static void ping_end(ObClient *client, gpointer data)
+{
+ ObPingTarget *t;
+
+ if ((t = g_hash_table_find(ping_ids, find_client, client))) {
+ g_hash_table_remove(ping_ids, &t->id);
+
+ g_source_remove(t->loopid);
+
+ g_slice_free(ObPingTarget, t);
+ }
+}
diff --git a/openbox/ping.h b/openbox/ping.h
new file mode 100644
index 0000000..ceb0bdb
--- /dev/null
+++ b/openbox/ping.h
@@ -0,0 +1,43 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ client.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2008 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __ping_h
+#define __ping_h
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+struct _ObClient;
+
+/*!
+ Notifies when the client application isn't responding to pings, or when it
+ starts responding again.
+ @param dead TRUE if the app isn't responding, FALSE if it starts responding
+ again
+*/
+typedef void (*ObPingEventHandler) (struct _ObClient *c, gboolean dead);
+
+void ping_startup(gboolean reconfigure);
+void ping_shutdown(gboolean reconfigure);
+
+void ping_start(struct _ObClient *c, ObPingEventHandler h);
+
+void ping_got_pong(guint32 id);
+
+#endif
diff --git a/openbox/place.c b/openbox/place.c
new file mode 100644
index 0000000..2cd21bb
--- /dev/null
+++ b/openbox/place.c
@@ -0,0 +1,593 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ place.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "client.h"
+#include "group.h"
+#include "screen.h"
+#include "frame.h"
+#include "focus.h"
+#include "config.h"
+#include "dock.h"
+#include "debug.h"
+
+extern ObDock *dock;
+
+static Rect *pick_pointer_head(ObClient *c)
+{
+ return screen_area(c->desktop, screen_monitor_pointer(), NULL);
+}
+
+/* use the following priority lists for pick_head()
+
+ When a window is being placed in the FOREGROUND, use a monitor chosen in
+ the following order:
+ 1. per-app settings
+ 2. same monitor as parent
+ 3. primary monitor if placement=PRIMARY
+ active monitor if placement=ACTIVE
+ pointer monitor if placement=MOUSE
+ 4. primary monitor
+ 5. other monitors where the window has group members on the same desktop
+ 6. other monitors where the window has group members on other desktops
+ 7. other monitors
+
+ When a window is being placed in the BACKGROUND, use a monitor chosen in the
+ following order:
+ 1. per-app settings
+ 2. same monitor as parent
+ 3. other monitors where the window has group members on the same desktop
+ 3a. primary monitor in this set
+ 3b. other monitors in this set
+ 4. other monitors where the window has group members on other desktops
+ 4a. primary monitor in this set
+ 4b. other monitors in this set
+ 5. other monitors
+ 5a. primary monitor in this set
+ 5b. other monitors in this set
+*/
+
+/*! One for each possible head, used to sort them in order of precedence. */
+typedef struct {
+ guint monitor;
+ guint flags;
+} ObPlaceHead;
+
+/*! Flags for ObPlaceHead */
+enum {
+ HEAD_PARENT = 1 << 0, /* parent's monitor */
+ HEAD_PLACED = 1 << 1, /* chosen monitor by placement */
+ HEAD_PRIMARY = 1 << 2, /* primary monitor */
+ HEAD_GROUP_DESK = 1 << 3, /* has a group member on the same desktop */
+ HEAD_GROUP = 1 << 4, /* has a group member on another desktop */
+ HEAD_PERAPP = 1 << 5, /* chosen by per-app settings */
+};
+
+gint cmp_foreground(const void *a, const void *b)
+{
+ const ObPlaceHead *h1 = a;
+ const ObPlaceHead *h2 = b;
+ gint i = 0;
+
+ if (h1->monitor == h2->monitor) return 0;
+
+ if (h1->flags & HEAD_PERAPP) --i;
+ if (h2->flags & HEAD_PERAPP) ++i;
+ if (i) return i;
+
+ if (h1->flags & HEAD_PARENT) --i;
+ if (h2->flags & HEAD_PARENT) ++i;
+ if (i) return i;
+
+ if (h1->flags & HEAD_PLACED) --i;
+ if (h2->flags & HEAD_PLACED) ++i;
+ if (i) return i;
+
+ if (h1->flags & HEAD_PRIMARY) --i;
+ if (h2->flags & HEAD_PRIMARY) ++i;
+ if (i) return i;
+
+ if (h1->flags & HEAD_GROUP_DESK) --i;
+ if (h2->flags & HEAD_GROUP_DESK) ++i;
+ if (i) return i;
+
+ if (h1->flags & HEAD_GROUP) --i;
+ if (h2->flags & HEAD_GROUP) ++i;
+ if (i) return i;
+
+ return h1->monitor - h2->monitor;
+}
+
+gint cmp_background(const void *a, const void *b)
+{
+ const ObPlaceHead *h1 = a;
+ const ObPlaceHead *h2 = b;
+ gint i = 0;
+
+ if (h1->monitor == h2->monitor) return 0;
+
+ if (h1->flags & HEAD_PERAPP) --i;
+ if (h2->flags & HEAD_PERAPP) ++i;
+ if (i) return i;
+
+ if (h1->flags & HEAD_PARENT) --i;
+ if (h2->flags & HEAD_PARENT) ++i;
+ if (i) return i;
+
+ if (h1->flags & HEAD_GROUP_DESK || h2->flags & HEAD_GROUP_DESK) {
+ if (h1->flags & HEAD_GROUP_DESK) --i;
+ if (h2->flags & HEAD_GROUP_DESK) ++i;
+ if (i) return i;
+ if (h1->flags & HEAD_PRIMARY) --i;
+ if (h2->flags & HEAD_PRIMARY) ++i;
+ if (i) return i;
+ }
+
+ if (h1->flags & HEAD_GROUP || h2->flags & HEAD_GROUP) {
+ if (h1->flags & HEAD_GROUP) --i;
+ if (h2->flags & HEAD_GROUP) ++i;
+ if (i) return i;
+ if (h1->flags & HEAD_PRIMARY) --i;
+ if (h2->flags & HEAD_PRIMARY) ++i;
+ if (i) return i;
+ }
+
+ if (h1->flags & HEAD_PRIMARY) --i;
+ if (h2->flags & HEAD_PRIMARY) ++i;
+ if (i) return i;
+
+ return h1->monitor - h2->monitor;
+}
+
+/*! Pick a monitor to place a window on. */
+static Rect *pick_head(ObClient *c, gboolean foreground,
+ ObAppSettings *settings)
+{
+ Rect *area;
+ ObPlaceHead *choice;
+ guint i;
+ ObClient *p;
+ GSList *it;
+
+ choice = g_new(ObPlaceHead, screen_num_monitors);
+ for (i = 0; i < screen_num_monitors; ++i) {
+ choice[i].monitor = i;
+ choice[i].flags = 0;
+ }
+
+ /* find monitors with group members */
+ if (c->group) {
+ for (it = c->group->members; it; it = g_slist_next(it)) {
+ ObClient *itc = it->data;
+ if (itc != c) {
+ guint m = client_monitor(itc);
+
+ if (m < screen_num_monitors) {
+ if (screen_compare_desktops(itc->desktop, c->desktop))
+ choice[m].flags |= HEAD_GROUP_DESK;
+ else
+ choice[m].flags |= HEAD_GROUP;
+ }
+ }
+ }
+ }
+
+ i = screen_monitor_primary(FALSE);
+ if (i < screen_num_monitors) {
+ choice[i].flags |= HEAD_PRIMARY;
+ if (config_place_monitor == OB_PLACE_MONITOR_PRIMARY)
+ choice[i].flags |= HEAD_PLACED;
+ if (settings &&
+ settings->monitor_type == OB_PLACE_MONITOR_PRIMARY)
+ choice[i].flags |= HEAD_PERAPP;
+ }
+
+ i = screen_monitor_active();
+ if (i < screen_num_monitors) {
+ if (config_place_monitor == OB_PLACE_MONITOR_ACTIVE)
+ choice[i].flags |= HEAD_PLACED;
+ if (settings &&
+ settings->monitor_type == OB_PLACE_MONITOR_ACTIVE)
+ choice[i].flags |= HEAD_PERAPP;
+ }
+
+ i = screen_monitor_pointer();
+ if (i < screen_num_monitors) {
+ if (config_place_monitor == OB_PLACE_MONITOR_MOUSE)
+ choice[i].flags |= HEAD_PLACED;
+ if (settings &&
+ settings->monitor_type == OB_PLACE_MONITOR_MOUSE)
+ choice[i].flags |= HEAD_PERAPP;
+ }
+
+ if (settings) {
+ i = settings->monitor - 1;
+ if (i < screen_num_monitors)
+ choice[i].flags |= HEAD_PERAPP;
+ }
+
+ /* direct parent takes highest precedence */
+ if ((p = client_direct_parent(c))) {
+ i = client_monitor(p);
+ if (i < screen_num_monitors)
+ choice[i].flags |= HEAD_PARENT;
+ }
+
+ qsort(choice, screen_num_monitors, sizeof(ObPlaceHead),
+ foreground ? cmp_foreground : cmp_background);
+
+ /* save the areas of the monitors in order of their being chosen */
+ for (i = 0; i < screen_num_monitors; ++i)
+ {
+ ob_debug("placement choice %d is monitor %d", i, choice[i].monitor);
+ if (choice[i].flags & HEAD_PARENT)
+ ob_debug(" - parent on monitor");
+ if (choice[i].flags & HEAD_PLACED)
+ ob_debug(" - placement choice");
+ if (choice[i].flags & HEAD_PRIMARY)
+ ob_debug(" - primary monitor");
+ if (choice[i].flags & HEAD_GROUP_DESK)
+ ob_debug(" - group on same desktop");
+ if (choice[i].flags & HEAD_GROUP)
+ ob_debug(" - group on other desktop");
+ }
+
+ area = screen_area(c->desktop, choice[0].monitor, NULL);
+
+ g_free(choice);
+
+ /* return the area for the chosen monitor */
+ return area;
+}
+
+static gboolean place_random(ObClient *client, Rect *area, gint *x, gint *y)
+{
+ 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;
+}
+
+static gboolean place_under_mouse(ObClient *client, gint *x, gint *y)
+{
+ gint l, r, t, b;
+ gint px, py;
+ Rect *area;
+
+ ob_debug("placing under mouse");
+
+ if (!screen_pointer_pos(&px, &py))
+ return FALSE;
+ area = pick_pointer_head(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;
+
+ *x = px - client->area.width / 2 - client->frame->size.left;
+ *x = MIN(MAX(*x, l), r);
+ *y = py - client->area.height / 2 - client->frame->size.top;
+ *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)
+{
+ if (!settings || (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 -
+ settings->position.x.pos;
+ else
+ *x = screen->x + settings->position.x.pos;
+ if (settings->position.x.denom)
+ *x = (*x * screen->width) / settings->position.x.denom;
+
+ 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 -
+ settings->position.y.pos;
+ else
+ *y = screen->y + settings->position.y.pos;
+ if (settings->position.y.denom)
+ *y = (*y * screen->height) / settings->position.y.denom;
+
+ return TRUE;
+}
+
+static gboolean place_transient_splash(ObClient *client, Rect *area,
+ gint *x, gint *y)
+{
+ if (client->type == OB_CLIENT_TYPE_DIALOG) {
+ GSList *it;
+ gboolean first = TRUE;
+ gint l, r, t, b;
+
+ ob_debug("placing dialog");
+
+ for (it = client->parents; it; it = g_slist_next(it)) {
+ ObClient *m = it->data;
+ if (!m->iconic) {
+ if (first) {
+ l = RECT_LEFT(m->frame->area);
+ t = RECT_TOP(m->frame->area);
+ r = RECT_RIGHT(m->frame->area);
+ b = RECT_BOTTOM(m->frame->area);
+ first = FALSE;
+ } else {
+ l = MIN(l, RECT_LEFT(m->frame->area));
+ t = MIN(t, RECT_TOP(m->frame->area));
+ r = MAX(r, RECT_RIGHT(m->frame->area));
+ b = MAX(b, RECT_BOTTOM(m->frame->area));
+ }
+ }
+ if (!first) {
+ *x = ((r + 1 - l) - client->frame->area.width) / 2 + l;
+ *y = ((b + 1 - t) - client->frame->area.height) / 2 + t;
+ return TRUE;
+ }
+ }
+ }
+
+ if (client->type == OB_CLIENT_TYPE_DIALOG ||
+ client->type == OB_CLIENT_TYPE_SPLASH)
+ {
+ 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;
+ 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)
+{
+ Rect *area;
+ gboolean ret;
+
+ /* 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)))
+ return FALSE;
+
+ area = pick_head(client, foreground, settings);
+
+ /* 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);
+ g_assert(ret);
+
+ g_slice_free(Rect, area);
+
+ /* get where the client should be */
+ frame_frame_gravity(client->frame, x, y);
+ return TRUE;
+}
diff --git a/openbox/place.h b/openbox/place.h
new file mode 100644
index 0000000..94e2dc0
--- /dev/null
+++ b/openbox/place.h
@@ -0,0 +1,45 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ place.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef ob__place_h
+#define ob__place_h
+
+#include <glib.h>
+
+struct _ObClient;
+struct _ObAppSettings;
+
+typedef enum
+{
+ OB_PLACE_POLICY_SMART,
+ OB_PLACE_POLICY_MOUSE
+} ObPlacePolicy;
+
+typedef enum
+{
+ OB_PLACE_MONITOR_ANY,
+ OB_PLACE_MONITOR_ACTIVE,
+ OB_PLACE_MONITOR_MOUSE,
+ OB_PLACE_MONITOR_PRIMARY
+} ObPlaceMonitor;
+
+gboolean place_client(struct _ObClient *client, gboolean foreground,
+ gint *x, gint *y, struct _ObAppSettings *settings);
+
+#endif
diff --git a/openbox/popup.c b/openbox/popup.c
new file mode 100644
index 0000000..2a83d7b
--- /dev/null
+++ b/openbox/popup.c
@@ -0,0 +1,569 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ popup.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "popup.h"
+
+#include "openbox.h"
+#include "frame.h"
+#include "client.h"
+#include "stacking.h"
+#include "event.h"
+#include "screen.h"
+#include "obrender/render.h"
+#include "obrender/theme.h"
+
+ObPopup *popup_new(void)
+{
+ XSetWindowAttributes attrib;
+ ObPopup *self = g_slice_new0(ObPopup);
+
+ self->obwin.type = OB_WINDOW_CLASS_INTERNAL;
+ self->gravity = NorthWestGravity;
+ self->x = self->y = self->textw = self->h = 0;
+ self->a_bg = RrAppearanceCopy(ob_rr_theme->osd_bg);
+ self->a_text = RrAppearanceCopy(ob_rr_theme->osd_hilite_label);
+ self->iconwm = self->iconhm = 1;
+
+ attrib.override_redirect = True;
+ self->bg = XCreateWindow(obt_display, obt_root(ob_screen),
+ 0, 0, 1, 1, 0, RrDepth(ob_rr_inst),
+ InputOutput, RrVisual(ob_rr_inst),
+ CWOverrideRedirect, &attrib);
+
+ self->text = XCreateWindow(obt_display, self->bg,
+ 0, 0, 1, 1, 0, RrDepth(ob_rr_inst),
+ InputOutput, RrVisual(ob_rr_inst), 0, NULL);
+
+ XSetWindowBorderWidth(obt_display, self->bg, ob_rr_theme->obwidth);
+ XSetWindowBorder(obt_display, self->bg,
+ RrColorPixel(ob_rr_theme->osd_border_color));
+
+ XMapWindow(obt_display, self->text);
+
+ stacking_add(INTERNAL_AS_WINDOW(self));
+ window_add(&self->bg, INTERNAL_AS_WINDOW(self));
+ return self;
+}
+
+void popup_free(ObPopup *self)
+{
+ if (self) {
+ popup_hide(self); /* make sure it's not showing or is being delayed and
+ will be shown */
+ XDestroyWindow(obt_display, self->bg);
+ XDestroyWindow(obt_display, self->text);
+ RrAppearanceFree(self->a_bg);
+ RrAppearanceFree(self->a_text);
+ window_remove(self->bg);
+ stacking_remove(self);
+ g_slice_free(ObPopup, self);
+ }
+}
+
+void popup_position(ObPopup *self, gint gravity, gint x, gint y)
+{
+ self->gravity = gravity;
+ self->x = x;
+ self->y = y;
+}
+
+void popup_text_width(ObPopup *self, gint w)
+{
+ self->textw = w;
+}
+
+void popup_min_width(ObPopup *self, gint minw)
+{
+ self->minw = minw;
+}
+
+void popup_max_width(ObPopup *self, gint maxw)
+{
+ self->maxw = maxw;
+}
+
+void popup_height(ObPopup *self, gint h)
+{
+ gint texth;
+
+ /* don't let the height be smaller than the text */
+ texth = RrMinHeight(self->a_text) + ob_rr_theme->paddingy * 2;
+ self->h = MAX(h, texth);
+}
+
+void popup_text_width_to_string(ObPopup *self, gchar *text)
+{
+ if (text[0] != '\0') {
+ self->a_text->texture[0].data.text.string = text;
+ self->textw = RrMinWidth(self->a_text);
+ } else
+ self->textw = 0;
+}
+
+void popup_height_to_string(ObPopup *self, gchar *text)
+{
+ self->h = RrMinHeight(self->a_text) + ob_rr_theme->paddingy * 2;
+}
+
+void popup_text_width_to_strings(ObPopup *self, gchar **strings, gint num)
+{
+ gint i, maxw;
+
+ maxw = 0;
+ for (i = 0; i < num; ++i) {
+ popup_text_width_to_string(self, strings[i]);
+ maxw = MAX(maxw, self->textw);
+ }
+ self->textw = maxw;
+}
+
+void popup_set_text_align(ObPopup *self, RrJustify align)
+{
+ self->a_text->texture[0].data.text.justify = align;
+}
+
+static gboolean popup_show_timeout(gpointer data)
+{
+ ObPopup *self = data;
+
+ XMapWindow(obt_display, self->bg);
+ stacking_raise(INTERNAL_AS_WINDOW(self));
+ self->mapped = TRUE;
+ self->delay_mapped = FALSE;
+ self->delay_timer = 0;
+
+ return FALSE; /* don't repeat */
+}
+
+void popup_delay_show(ObPopup *self, gulong msec, gchar *text)
+{
+ gint l, t, r, b;
+ gint x, y, w, h;
+ guint m;
+ gint emptyx, emptyy; /* empty space between elements */
+ gint textx, texty, textw, texth;
+ gint iconx, icony, iconw, iconh;
+ const Rect *area;
+ Rect mon;
+ gboolean hasicon = self->hasicon;
+
+ /* when there is no icon and the text is not parent relative, then
+ fill the whole dialog with the text appearance, don't use the bg at all
+ */
+ if (hasicon || self->a_text->surface.grad == RR_SURFACE_PARENTREL)
+ RrMargins(self->a_bg, &l, &t, &r, &b);
+ else
+ l = t = r = b = 0;
+
+ /* set up the textures */
+ self->a_text->texture[0].data.text.string = text;
+
+ /* measure the text out */
+ if (text[0] != '\0') {
+ RrMinSize(self->a_text, &textw, &texth);
+ } else {
+ textw = 0;
+ texth = RrMinHeight(self->a_text);
+ }
+
+ /* get the height, which is also used for the icon width */
+ emptyy = t + b + ob_rr_theme->paddingy * 2;
+ if (self->h)
+ texth = self->h - emptyy;
+ h = texth * self->iconhm + emptyy;
+
+ if (self->textw)
+ textw = self->textw;
+
+ iconx = textx = l + ob_rr_theme->paddingx;
+
+ emptyx = l + r + ob_rr_theme->paddingx * 2;
+ if (hasicon) {
+ iconw = texth * self->iconwm;
+ iconh = texth * self->iconhm;
+ textx += iconw + ob_rr_theme->paddingx;
+ if (textw)
+ emptyx += ob_rr_theme->paddingx; /* between the icon and text */
+ icony = (h - iconh - emptyy) / 2 + t + ob_rr_theme->paddingy;
+ } else
+ iconw = 0;
+
+ texty = (h - texth - emptyy) / 2 + t + ob_rr_theme->paddingy;
+
+ /* when there is no icon, then fill the whole dialog with the text
+ appearance
+ */
+ if (!hasicon)
+ {
+ textx = texty = 0;
+ texth += emptyy;
+ textw += emptyx;
+ emptyx = emptyy = 0;
+ }
+
+ w = textw + emptyx + iconw;
+ /* cap it at maxw/minw */
+ if (self->maxw) w = MIN(w, self->maxw);
+ if (self->minw) w = MAX(w, self->minw);
+ textw = w - emptyx - iconw;
+
+ /* sanity checks to avoid crashes! */
+ if (w < 1) w = 1;
+ if (h < 1) h = 1;
+ if (texth < 1) texth = 1;
+
+ /* set up the x coord */
+ x = self->x;
+ switch (self->gravity) {
+ case NorthGravity: case CenterGravity: case SouthGravity:
+ x -= w / 2;
+ break;
+ case NorthEastGravity: case EastGravity: case SouthEastGravity:
+ x -= w;
+ break;
+ }
+
+ /* set up the y coord */
+ y = self->y;
+ switch (self->gravity) {
+ case WestGravity: case CenterGravity: case EastGravity:
+ y -= h / 2;
+ break;
+ case SouthWestGravity: case SouthGravity: case SouthEastGravity:
+ y -= h;
+ break;
+ }
+
+ /* Find the monitor which contains the biggest part of the popup.
+ * If the popup is completely off screen, limit it to the intersection
+ * of all monitors and then try again. If it's still off screen, put it
+ * on monitor 0. */
+ RECT_SET(mon, x, y, w, h);
+ m = screen_find_monitor(&mon);
+ area = screen_physical_area_monitor(m);
+
+ x=MAX(MIN(x, area->x+area->width-w),area->x);
+ y=MAX(MIN(y, area->y+area->height-h),area->y);
+
+ if (m == screen_num_monitors) {
+ RECT_SET(mon, x, y, w, h);
+ m = screen_find_monitor(&mon);
+ if (m == screen_num_monitors)
+ m = 0;
+ area = screen_physical_area_monitor(m);
+
+ x=MAX(MIN(x, area->x+area->width-w),area->x);
+ y=MAX(MIN(y, area->y+area->height-h),area->y);
+ }
+
+ /* set the windows/appearances up */
+ XMoveResizeWindow(obt_display, self->bg, x, y, w, h);
+ /* when there is no icon and the text is not parent relative, then
+ fill the whole dialog with the text appearance, don't use the bg at all
+ */
+ if (hasicon || self->a_text->surface.grad == RR_SURFACE_PARENTREL)
+ RrPaint(self->a_bg, self->bg, w, h);
+
+ if (textw) {
+ self->a_text->surface.parent = self->a_bg;
+ self->a_text->surface.parentx = textx;
+ self->a_text->surface.parenty = texty;
+ XMoveResizeWindow(obt_display, self->text, textx, texty, textw, texth);
+ RrPaint(self->a_text, self->text, textw, texth);
+ }
+
+ if (hasicon)
+ self->draw_icon(iconx, icony, iconw, iconh, self->draw_icon_data);
+
+ /* do the actual showing */
+ if (!self->mapped) {
+ if (msec) {
+ /* don't kill previous show timers */
+ if (!self->delay_mapped) {
+ self->delay_timer =
+ g_timeout_add(msec, popup_show_timeout, self);
+ self->delay_mapped = TRUE;
+ }
+ } else {
+ popup_show_timeout(self);
+ }
+ }
+}
+
+void popup_hide(ObPopup *self)
+{
+ if (self->mapped) {
+ gulong ignore_start;
+
+ /* kill enter events cause by this unmapping */
+ ignore_start = event_start_ignore_all_enters();
+
+ XUnmapWindow(obt_display, self->bg);
+ self->mapped = FALSE;
+
+ event_end_ignore_all_enters(ignore_start);
+ } else if (self->delay_mapped) {
+ g_source_remove(self->delay_timer);
+ self->delay_timer = 0;
+ self->delay_mapped = FALSE;
+ }
+}
+
+static void icon_popup_draw_icon(gint x, gint y, gint w, gint h, gpointer data)
+{
+ ObIconPopup *self = data;
+
+ self->a_icon->surface.parent = self->popup->a_bg;
+ self->a_icon->surface.parentx = x;
+ self->a_icon->surface.parenty = y;
+ XMoveResizeWindow(obt_display, self->icon, x, y, w, h);
+ RrPaint(self->a_icon, self->icon, w, h);
+}
+
+ObIconPopup *icon_popup_new(void)
+{
+ ObIconPopup *self;
+
+ self = g_slice_new0(ObIconPopup);
+ self->popup = popup_new();
+ self->a_icon = RrAppearanceCopy(ob_rr_theme->a_clear_tex);
+ self->icon = XCreateWindow(obt_display, self->popup->bg,
+ 0, 0, 1, 1, 0,
+ RrDepth(ob_rr_inst), InputOutput,
+ RrVisual(ob_rr_inst), 0, NULL);
+ XMapWindow(obt_display, self->icon);
+
+ self->popup->hasicon = TRUE;
+ self->popup->draw_icon = icon_popup_draw_icon;
+ self->popup->draw_icon_data = self;
+
+ return self;
+}
+
+void icon_popup_free(ObIconPopup *self)
+{
+ if (self) {
+ XDestroyWindow(obt_display, self->icon);
+ RrAppearanceFree(self->a_icon);
+ popup_free(self->popup);
+ g_slice_free(ObIconPopup, self);
+ }
+}
+
+void icon_popup_delay_show(ObIconPopup *self, gulong msec,
+ gchar *text, RrImage *icon)
+{
+ if (icon) {
+ RrAppearanceClearTextures(self->a_icon);
+ self->a_icon->texture[0].type = RR_TEXTURE_IMAGE;
+ self->a_icon->texture[0].data.image.alpha = 0xff;
+ self->a_icon->texture[0].data.image.image = icon;
+ } else {
+ RrAppearanceClearTextures(self->a_icon);
+ self->a_icon->texture[0].type = RR_TEXTURE_NONE;
+ }
+
+ popup_delay_show(self->popup, msec, text);
+}
+
+void icon_popup_icon_size_multiplier(ObIconPopup *self, guint wm, guint hm)
+{
+ /* cap them at 1 */
+ self->popup->iconwm = MAX(1, wm);
+ self->popup->iconhm = MAX(1, hm);
+}
+
+static void pager_popup_draw_icon(gint px, gint py, gint w, gint h,
+ gpointer data)
+{
+ ObPagerPopup *self = data;
+ gint x, y;
+ guint rown, n;
+ guint horz_inc;
+ guint vert_inc;
+ guint r, c;
+ gint eachw, eachh;
+ const guint cols = screen_desktop_layout.columns;
+ const guint rows = screen_desktop_layout.rows;
+ const gint linewidth = ob_rr_theme->obwidth;
+
+ eachw = (w - ((cols + 1) * linewidth)) / cols;
+ eachh = (h - ((rows + 1) * linewidth)) / rows;
+ /* make them squares */
+ eachw = eachh = MIN(eachw, eachh);
+
+ /* center */
+ px += (w - (cols * (eachw + linewidth) + linewidth)) / 2;
+ py += (h - (rows * (eachh + linewidth) + linewidth)) / 2;
+
+ if (eachw <= 0 || eachh <= 0)
+ return;
+
+ switch (screen_desktop_layout.orientation) {
+ case OB_ORIENTATION_HORZ:
+ switch (screen_desktop_layout.start_corner) {
+ case OB_CORNER_TOPLEFT:
+ n = 0;
+ horz_inc = 1;
+ vert_inc = cols;
+ break;
+ case OB_CORNER_TOPRIGHT:
+ n = cols - 1;
+ horz_inc = -1;
+ vert_inc = cols;
+ break;
+ case OB_CORNER_BOTTOMRIGHT:
+ n = rows * cols - 1;
+ horz_inc = -1;
+ vert_inc = -screen_desktop_layout.columns;
+ break;
+ case OB_CORNER_BOTTOMLEFT:
+ n = (rows - 1) * cols;
+ horz_inc = 1;
+ vert_inc = -cols;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ break;
+ case OB_ORIENTATION_VERT:
+ switch (screen_desktop_layout.start_corner) {
+ case OB_CORNER_TOPLEFT:
+ n = 0;
+ horz_inc = rows;
+ vert_inc = 1;
+ break;
+ case OB_CORNER_TOPRIGHT:
+ n = rows * (cols - 1);
+ horz_inc = -rows;
+ vert_inc = 1;
+ break;
+ case OB_CORNER_BOTTOMRIGHT:
+ n = rows * cols - 1;
+ horz_inc = -rows;
+ vert_inc = -1;
+ break;
+ case OB_CORNER_BOTTOMLEFT:
+ n = rows - 1;
+ horz_inc = rows;
+ vert_inc = -1;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ rown = n;
+ for (r = 0, y = 0; r < rows; ++r, y += eachh + linewidth)
+ {
+ for (c = 0, x = 0; c < cols; ++c, x += eachw + linewidth)
+ {
+ RrAppearance *a;
+
+ if (n < self->desks) {
+ a = (n == self->curdesk ? self->hilight : self->unhilight);
+
+ a->surface.parent = self->popup->a_bg;
+ a->surface.parentx = x + px;
+ a->surface.parenty = y + py;
+ XMoveResizeWindow(obt_display, self->wins[n],
+ x + px, y + py, eachw, eachh);
+ RrPaint(a, self->wins[n], eachw, eachh);
+ }
+ n += horz_inc;
+ }
+ n = rown += vert_inc;
+ }
+}
+
+ObPagerPopup *pager_popup_new(void)
+{
+ ObPagerPopup *self;
+
+ self = g_slice_new(ObPagerPopup);
+ self->popup = popup_new();
+
+ self->desks = 0;
+ self->wins = g_new(Window, self->desks);
+ self->hilight = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg);
+ self->unhilight = RrAppearanceCopy(ob_rr_theme->osd_unhilite_bg);
+
+ self->popup->hasicon = TRUE;
+ self->popup->draw_icon = pager_popup_draw_icon;
+ self->popup->draw_icon_data = self;
+
+ return self;
+}
+
+void pager_popup_free(ObPagerPopup *self)
+{
+ if (self) {
+ guint i;
+
+ for (i = 0; i < self->desks; ++i)
+ XDestroyWindow(obt_display, self->wins[i]);
+ g_free(self->wins);
+ RrAppearanceFree(self->hilight);
+ RrAppearanceFree(self->unhilight);
+ popup_free(self->popup);
+ g_slice_free(ObPagerPopup, self);
+ }
+}
+
+void pager_popup_delay_show(ObPagerPopup *self, gulong msec,
+ gchar *text, guint desk)
+{
+ guint i;
+
+ if (screen_num_desktops < self->desks)
+ for (i = screen_num_desktops; i < self->desks; ++i)
+ XDestroyWindow(obt_display, self->wins[i]);
+
+ if (screen_num_desktops != self->desks)
+ self->wins = g_renew(Window, self->wins, screen_num_desktops);
+
+ if (screen_num_desktops > self->desks)
+ for (i = self->desks; i < screen_num_desktops; ++i) {
+ XSetWindowAttributes attr;
+
+ attr.border_pixel =
+ RrColorPixel(ob_rr_theme->osd_border_color);
+ self->wins[i] = XCreateWindow(obt_display, self->popup->bg,
+ 0, 0, 1, 1, ob_rr_theme->obwidth,
+ RrDepth(ob_rr_inst), InputOutput,
+ RrVisual(ob_rr_inst), CWBorderPixel,
+ &attr);
+ XMapWindow(obt_display, self->wins[i]);
+ }
+
+ self->desks = screen_num_desktops;
+ self->curdesk = desk;
+
+ popup_delay_show(self->popup, msec, text);
+}
+
+void pager_popup_icon_size_multiplier(ObPagerPopup *self, guint wm, guint hm)
+{
+ /* cap them at 1 */
+ self->popup->iconwm = MAX(1, wm);
+ self->popup->iconhm = MAX(1, hm);
+}
diff --git a/openbox/popup.h b/openbox/popup.h
new file mode 100644
index 0000000..6de9d18
--- /dev/null
+++ b/openbox/popup.h
@@ -0,0 +1,147 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ popup.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __popup_h
+#define __popup_h
+
+#include "window.h"
+#include "obrender/render.h"
+#include <glib.h>
+
+struct _ObClientIcon;
+
+#define POPUP_WIDTH 320
+#define POPUP_HEIGHT 48
+
+typedef struct _ObPopup ObPopup;
+typedef struct _ObIconPopup ObIconPopup;
+typedef struct _ObPagerPopup ObPagerPopup;
+
+struct _ObPopup
+{
+ ObWindow obwin;
+ Window bg;
+
+ Window text;
+
+ gboolean hasicon;
+ RrAppearance *a_bg;
+ RrAppearance *a_text;
+ gint gravity;
+ gint x;
+ gint y;
+ gint textw;
+ gint h;
+ gint minw;
+ gint maxw;
+ guint iconwm; /* icon width multiplier. multiplied by the normal width */
+ guint iconhm; /* icon height multiplier. multipled by the normal height */
+ gboolean mapped;
+ gboolean delay_mapped;
+ guint delay_timer;
+
+ void (*draw_icon)(gint x, gint y, gint w, gint h, gpointer data);
+ gpointer draw_icon_data;
+};
+
+struct _ObIconPopup
+{
+ ObPopup *popup;
+
+ Window icon;
+ RrAppearance *a_icon;
+};
+
+struct _ObPagerPopup
+{
+ ObPopup *popup;
+
+ guint desks;
+ guint curdesk;
+ Window *wins;
+ RrAppearance *hilight;
+ RrAppearance *unhilight;
+};
+
+ObPopup *popup_new(void);
+void popup_free(ObPopup *self);
+
+/*! Position the popup. The gravity rules are not the same X uses for windows,
+ instead of the position being the top-left of the window, the gravity
+ specifies which corner of the popup will be placed at the given coords.
+ Static and Forget gravity are equivilent to NorthWest.
+*/
+void popup_position(ObPopup *self, gint gravity, gint x, gint y);
+/*! Set the sizes for the popup. When set to 0, the size will be based on
+ the text size. */
+void popup_height(ObPopup *self, gint w);
+void popup_min_width(ObPopup *self, gint minw);
+void popup_max_width(ObPopup *self, gint maxw);
+void popup_text_width(ObPopup *self, gint w);
+void popup_text_width_to_string(ObPopup *self, gchar *text);
+void popup_height_to_string(ObPopup *self, gchar *text);
+void popup_text_width_to_strings(ObPopup *self, gchar **strings, gint num);
+
+void popup_set_text_align(ObPopup *self, RrJustify align);
+
+#define popup_show(s, t) popup_delay_show((s),0,(t))
+void popup_delay_show(ObPopup *self, gulong msec, gchar *text);
+void popup_hide(ObPopup *self);
+
+RrAppearance *popup_icon_appearance(ObPopup *self);
+
+
+ObIconPopup *icon_popup_new(void);
+void icon_popup_free(ObIconPopup *self);
+
+#define icon_popup_show(s, t, i) icon_popup_delay_show((s),0,(t),(i))
+void icon_popup_delay_show(ObIconPopup *self, gulong msec,
+ gchar *text, RrImage *icon);
+#define icon_popup_hide(p) popup_hide((p)->popup)
+#define icon_popup_position(p, g, x, y) popup_position((p)->popup,(g),(x),(y))
+#define icon_popup_text_width(p, w) popup_text_width((p)->popup,(w))
+#define icon_popup_height(p, h) popup_height((p)->popup,(h))
+#define icon_popup_min_width(p, m) popup_min_width((p)->popup,(m))
+#define icon_popup_max_width(p, m) popup_max_width((p)->popup,(m))
+#define icon_popup_text_width_to_string(p, s) \
+ popup_text_width_to_string((p)->popup,(s))
+#define icon_popup_text_width_to_strings(p, s, n) \
+ popup_text_width_to_strings((p)->popup,(s),(n))
+#define icon_popup_set_text_align(p, j) popup_set_text_align((p)->popup,(j))
+void icon_popup_icon_size_multiplier(ObIconPopup *self, guint wm, guint hm);
+
+ObPagerPopup *pager_popup_new(void);
+void pager_popup_free(ObPagerPopup *self);
+
+#define pager_popup_show(s, t, d) pager_popup_delay_show((s),0,(t),(d))
+void pager_popup_delay_show(ObPagerPopup *self, gulong msec,
+ gchar *text, guint desk);
+#define pager_popup_hide(p) popup_hide((p)->popup)
+#define pager_popup_position(p, g, x, y) popup_position((p)->popup,(g),(x),(y))
+#define pager_popup_text_width(p, w) popup_text_width((p)->popup,(w))
+#define pager_popup_height(p, h) popup_height((p)->popup,(h))
+#define pager_popup_min_width(p, m) popup_min_width((p)->popup,(m))
+#define pager_popup_max_width(p, m) popup_max_width((p)->popup,(m))
+#define pager_popup_text_width_to_string(p, s) \
+ popup_text_width_to_string((p)->popup,(s))
+#define pager_popup_text_width_to_strings(p, s, n) \
+ popup_text_width_to_strings((p)->popup,(s),(n))
+#define pager_popup_set_text_align(p, j) popup_set_text_align((p)->popup,(j))
+void pager_popup_icon_size_multiplier(ObPagerPopup *self, guint wm, guint hm);
+
+#endif
diff --git a/openbox/prompt.c b/openbox/prompt.c
new file mode 100644
index 0000000..1aa79d2
--- /dev/null
+++ b/openbox/prompt.c
@@ -0,0 +1,603 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ prompt.c for the Openbox window manager
+ Copyright (c) 2008 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "prompt.h"
+#include "openbox.h"
+#include "screen.h"
+#include "client.h"
+#include "group.h"
+#include "event.h"
+#include "obt/display.h"
+#include "obt/keyboard.h"
+#include "obt/prop.h"
+#include "gettext.h"
+
+static GList *prompt_list = NULL;
+
+/* we construct these */
+static RrAppearance *prompt_a_bg;
+static RrAppearance *prompt_a_button;
+static RrAppearance *prompt_a_focus;
+static RrAppearance *prompt_a_press;
+/* we change the max width which would screw with others */
+static RrAppearance *prompt_a_msg;
+
+/* sizing stuff */
+#define OUTSIDE_MARGIN 4
+#define MSG_BUTTON_SEPARATION 4
+#define BUTTON_SEPARATION 4
+#define BUTTON_VMARGIN 4
+#define BUTTON_HMARGIN 12
+#define MAX_WIDTH 400
+
+static void prompt_layout(ObPrompt *self);
+static void render_all(ObPrompt *self);
+static void render_button(ObPrompt *self, ObPromptElement *e);
+static void prompt_resize(ObPrompt *self, gint w, gint h);
+static void prompt_run_callback(ObPrompt *self, gint result);
+
+void prompt_startup(gboolean reconfig)
+{
+ /* note: this is not a copy, don't free it */
+ prompt_a_bg = ob_rr_theme->osd_bg;
+
+ prompt_a_button = RrAppearanceCopy(ob_rr_theme->osd_unpressed_button);
+ prompt_a_focus = RrAppearanceCopy(ob_rr_theme->osd_focused_button);
+ prompt_a_press = RrAppearanceCopy(ob_rr_theme->osd_pressed_button);
+
+ prompt_a_msg = RrAppearanceCopy(ob_rr_theme->osd_hilite_label);
+ prompt_a_msg->texture[0].data.text.flow = TRUE;
+
+ if (reconfig) {
+ GList *it;
+ for (it = prompt_list; it; it = g_list_next(it)) {
+ ObPrompt *p = it->data;
+ prompt_layout(p);
+ render_all(p);
+ }
+ }
+}
+
+void prompt_shutdown(gboolean reconfig)
+{
+ GList *it;
+
+ if (!reconfig) {
+ for (it = prompt_list; it; it = g_list_next(it)) {
+ ObPrompt *p = it->data;
+ if (p->cleanup) p->cleanup(p, p->data);
+ }
+
+ g_assert(prompt_list == NULL);
+ }
+
+ RrAppearanceFree(prompt_a_button);
+ RrAppearanceFree(prompt_a_focus);
+ RrAppearanceFree(prompt_a_press);
+ RrAppearanceFree(prompt_a_msg);
+}
+
+ObPrompt* prompt_new(const gchar *msg, const gchar *title,
+ const ObPromptAnswer *answers, gint n_answers,
+ gint default_result, gint cancel_result,
+ ObPromptCallback func, ObPromptCleanup cleanup,
+ gpointer data)
+{
+ ObPrompt *self;
+ XSetWindowAttributes attrib;
+ gint i;
+
+ attrib.override_redirect = FALSE;
+
+ self = g_slice_new0(ObPrompt);
+ self->ref = 1;
+ self->func = func;
+ self->cleanup = cleanup;
+ self->data = data;
+ self->default_result = default_result;
+ self->cancel_result = cancel_result;
+ self->super.type = OB_WINDOW_CLASS_PROMPT;
+ self->super.window = XCreateWindow(obt_display, obt_root(ob_screen),
+ 0, 0, 1, 1, 0,
+ CopyFromParent, InputOutput,
+ CopyFromParent,
+ CWOverrideRedirect,
+ &attrib);
+ self->ic = obt_keyboard_context_new(self->super.window,
+ self->super.window);
+
+ /* make it a dialog type window */
+ OBT_PROP_SET32(self->super.window, NET_WM_WINDOW_TYPE, ATOM,
+ OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG));
+
+ /* set the window's title */
+ if (title)
+ OBT_PROP_SETS(self->super.window, NET_WM_NAME, title);
+
+ /* listen for key presses on the window */
+ self->event_mask = KeyPressMask;
+
+ /* set up the text message widow */
+ self->msg.text = g_strdup(msg);
+ self->msg.window = XCreateWindow(obt_display, self->super.window,
+ 0, 0, 1, 1, 0,
+ CopyFromParent, InputOutput,
+ CopyFromParent, 0, NULL);
+ XMapWindow(obt_display, self->msg.window);
+
+ /* set up the buttons from the answers */
+
+ self->n_buttons = n_answers;
+ if (!self->n_buttons)
+ self->n_buttons = 1;
+
+ self->button = g_new0(ObPromptElement, self->n_buttons);
+
+ if (n_answers == 0) {
+ g_assert(self->n_buttons == 1); /* should be set to this above.. */
+ self->button[0].text = g_strdup(_("OK"));
+ }
+ else {
+ g_assert(self->n_buttons > 0);
+ for (i = 0; i < self->n_buttons; ++i) {
+ self->button[i].text = g_strdup(answers[i].text);
+ self->button[i].result = answers[i].result;
+ }
+ }
+
+ for (i = 0; i < self->n_buttons; ++i) {
+ self->button[i].window = XCreateWindow(obt_display, self->super.window,
+ 0, 0, 1, 1, 0,
+ CopyFromParent, InputOutput,
+ CopyFromParent, 0, NULL);
+ XMapWindow(obt_display, self->button[i].window);
+ window_add(&self->button[i].window, PROMPT_AS_WINDOW(self));
+
+ /* listen for button presses on the buttons */
+ XSelectInput(obt_display, self->button[i].window,
+ ButtonPressMask | ButtonReleaseMask | ButtonMotionMask);
+ }
+
+ prompt_list = g_list_prepend(prompt_list, self);
+
+ return self;
+}
+
+void prompt_ref(ObPrompt *self)
+{
+ ++self->ref;
+}
+
+void prompt_unref(ObPrompt *self)
+{
+ if (self && --self->ref == 0) {
+ gint i;
+
+ if (self->mapped)
+ prompt_hide(self);
+
+ prompt_list = g_list_remove(prompt_list, self);
+
+ obt_keyboard_context_unref(self->ic);
+
+ for (i = 0; i < self->n_buttons; ++i) {
+ window_remove(self->button[i].window);
+ XDestroyWindow(obt_display, self->button[i].window);
+ }
+
+ XDestroyWindow(obt_display, self->msg.window);
+ XDestroyWindow(obt_display, self->super.window);
+ g_slice_free(ObPrompt, self);
+ }
+}
+
+static void prompt_layout(ObPrompt *self)
+{
+ gint l, r, t, b;
+ gint i;
+ gint allbuttonsw, allbuttonsh, buttonx;
+ gint w, h;
+ gint maxw;
+
+ RrMargins(prompt_a_bg, &l, &t, &r, &b);
+ l += OUTSIDE_MARGIN;
+ t += OUTSIDE_MARGIN;
+ r += OUTSIDE_MARGIN;
+ b += OUTSIDE_MARGIN;
+
+ {
+ const Rect *area = screen_physical_area_all_monitors();
+ maxw = MIN(MAX_WIDTH, area->width*4/5);
+ }
+
+ /* find the button sizes and how much space we need for them */
+ allbuttonsw = allbuttonsh = 0;
+ for (i = 0; i < self->n_buttons; ++i) {
+ gint bw, bh;
+
+ prompt_a_button->texture[0].data.text.string = self->button[i].text;
+ prompt_a_focus->texture[0].data.text.string = self->button[i].text;
+ prompt_a_press->texture[0].data.text.string = self->button[i].text;
+ RrMinSize(prompt_a_button, &bw, &bh);
+ self->button[i].width = bw;
+ self->button[i].height = bh;
+ RrMinSize(prompt_a_focus, &bw, &bh);
+ self->button[i].width = MAX(self->button[i].width, bw);
+ self->button[i].height = MAX(self->button[i].height, bh);
+ RrMinSize(prompt_a_press, &bw, &bh);
+ self->button[i].width = MAX(self->button[i].width, bw);
+ self->button[i].height = MAX(self->button[i].height, bh);
+
+ self->button[i].width += BUTTON_HMARGIN * 2;
+ self->button[i].height += BUTTON_VMARGIN * 2;
+
+ allbuttonsw += self->button[i].width + (i > 0 ? BUTTON_SEPARATION : 0);
+ allbuttonsh = MAX(allbuttonsh, self->button[i].height);
+ }
+
+ self->msg_wbound = MAX(allbuttonsw, maxw);
+
+ /* measure the text message area */
+ prompt_a_msg->texture[0].data.text.string = self->msg.text;
+ prompt_a_msg->texture[0].data.text.maxwidth = self->msg_wbound;
+ RrMinSize(prompt_a_msg, &self->msg.width, &self->msg.height);
+
+ /* width and height inside the outer margins */
+ w = MAX(self->msg.width, allbuttonsw);
+ h = self->msg.height + MSG_BUTTON_SEPARATION + allbuttonsh;
+
+ /* position the text message */
+ self->msg.x = l + (w - self->msg.width) / 2;
+ self->msg.y = t;
+
+ /* position the button buttons on the right of the dialog */
+ buttonx = l + w;
+ for (i = self->n_buttons - 1; i >= 0; --i) {
+ self->button[i].x = buttonx - self->button[i].width;
+ buttonx -= self->button[i].width + BUTTON_SEPARATION;
+ self->button[i].y = t + h - allbuttonsh;
+ self->button[i].y += (allbuttonsh - self->button[i].height) / 2;
+ }
+
+ /* size and position the toplevel window */
+ prompt_resize(self, w + l + r, h + t + b);
+
+ /* move and resize the internal windows */
+ XMoveResizeWindow(obt_display, self->msg.window,
+ self->msg.x, self->msg.y,
+ self->msg.width, self->msg.height);
+ for (i = 0; i < self->n_buttons; ++i)
+ XMoveResizeWindow(obt_display, self->button[i].window,
+ self->button[i].x, self->button[i].y,
+ self->button[i].width, self->button[i].height);
+}
+
+static void prompt_resize(ObPrompt *self, gint w, gint h)
+{
+ XConfigureRequestEvent req;
+ XSizeHints hints;
+
+ self->width = w;
+ self->height = h;
+
+ /* the user can't resize the prompt */
+ hints.flags = PMinSize | PMaxSize;
+ hints.min_width = hints.max_width = w;
+ hints.min_height = hints.max_height = h;
+ XSetWMNormalHints(obt_display, self->super.window, &hints);
+
+ if (self->mapped) {
+ /* send a configure request like a normal client would */
+ req.type = ConfigureRequest;
+ req.display = obt_display;
+ req.parent = obt_root(ob_screen);
+ req.window = self->super.window;
+ req.width = w;
+ req.height = h;
+ req.value_mask = CWWidth | CWHeight;
+ XSendEvent(req.display, req.window, FALSE, StructureNotifyMask,
+ (XEvent*)&req);
+ }
+ else
+ XResizeWindow(obt_display, self->super.window, w, h);
+}
+
+static void setup_button_focus_tex(ObPromptElement *e, RrAppearance *a,
+ gboolean on)
+{
+ gint i, l, r, t, b;
+
+ for (i = 1; i < 5; ++i)
+ a->texture[i].type = on ? RR_TEXTURE_LINE_ART : RR_TEXTURE_NONE;
+
+ if (!on) return;
+
+ RrMargins(a, &l, &t, &r, &b);
+ l += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2;
+ r += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2;
+ t += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2;
+ b += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2;
+
+ /* top line */
+ a->texture[1].data.lineart.x1 = l;
+ a->texture[1].data.lineart.x2 = e->width - r - 1;
+ a->texture[1].data.lineart.y1 = t;
+ a->texture[1].data.lineart.y2 = t;
+
+ /* bottom line */
+ a->texture[2].data.lineart.x1 = l;
+ a->texture[2].data.lineart.x2 = e->width - r - 1;
+ a->texture[2].data.lineart.y1 = e->height - b - 1;
+ a->texture[2].data.lineart.y2 = e->height - b - 1;
+
+ /* left line */
+ a->texture[3].data.lineart.x1 = l;
+ a->texture[3].data.lineart.x2 = l;
+ a->texture[3].data.lineart.y1 = t;
+ a->texture[3].data.lineart.y2 = e->height - b - 1;
+
+ /* right line */
+ a->texture[4].data.lineart.x1 = e->width - r - 1;
+ a->texture[4].data.lineart.x2 = e->width - r - 1;
+ a->texture[4].data.lineart.y1 = t;
+ a->texture[4].data.lineart.y2 = e->height - b - 1;
+}
+
+static void render_button(ObPrompt *self, ObPromptElement *e)
+{
+ RrAppearance *a;
+
+ if (e->hover && e->pressed) a = prompt_a_press;
+ else if (self->focus == e) a = prompt_a_focus;
+ else a = prompt_a_button;
+
+ a->surface.parent = prompt_a_bg;
+ a->surface.parentx = e->x;
+ a->surface.parenty = e->y;
+
+ /* draw the keyfocus line */
+ if (self->focus == e)
+ setup_button_focus_tex(e, a, TRUE);
+
+ a->texture[0].data.text.string = e->text;
+ RrPaint(a, e->window, e->width, e->height);
+
+ /* turn off the keyfocus line so that it doesn't affect size calculations
+ */
+ if (self->focus == e)
+ setup_button_focus_tex(e, a, FALSE);
+}
+
+static void render_all(ObPrompt *self)
+{
+ gint i;
+
+ RrPaint(prompt_a_bg, self->super.window, self->width, self->height);
+
+ prompt_a_msg->surface.parent = prompt_a_bg;
+ prompt_a_msg->surface.parentx = self->msg.x;
+ prompt_a_msg->surface.parenty = self->msg.y;
+
+ prompt_a_msg->texture[0].data.text.string = self->msg.text;
+ prompt_a_msg->texture[0].data.text.maxwidth = self->msg_wbound;
+ RrPaint(prompt_a_msg, self->msg.window, self->msg.width, self->msg.height);
+
+ for (i = 0; i < self->n_buttons; ++i)
+ render_button(self, &self->button[i]);
+}
+
+void prompt_show(ObPrompt *self, ObClient *parent, gboolean modal)
+{
+ gint i;
+
+ if (self->mapped) {
+ /* activate the prompt */
+ OBT_PROP_MSG(ob_screen, self->super.window, NET_ACTIVE_WINDOW,
+ 1, /* from an application.. */
+ event_time(),
+ 0,
+ 0, 0);
+ return;
+ }
+
+ /* set the focused button (if not found then the first button is used) */
+ self->focus = &self->button[0];
+ for (i = 0; i < self->n_buttons; ++i)
+ if (self->button[i].result == self->default_result) {
+ self->focus = &self->button[i];
+ break;
+ }
+
+ if (parent) {
+ Atom states[1];
+ gint nstates;
+ Window p;
+ XWMHints h;
+
+ if (parent->group) {
+ /* make it transient for the window's group */
+ h.flags = WindowGroupHint;
+ h.window_group = parent->group->leader;
+ p = obt_root(ob_screen);
+ }
+ else {
+ /* make it transient for the window directly */
+ h.flags = 0;
+ p = parent->window;
+ }
+
+ XSetWMHints(obt_display, self->super.window, &h);
+ OBT_PROP_SET32(self->super.window, WM_TRANSIENT_FOR, WINDOW, p);
+
+ states[0] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
+ nstates = (modal ? 1 : 0);
+ OBT_PROP_SETA32(self->super.window, NET_WM_STATE, ATOM,
+ states, nstates);
+ }
+ else
+ OBT_PROP_ERASE(self->super.window, WM_TRANSIENT_FOR);
+
+ /* set up the dialog and render it */
+ prompt_layout(self);
+ render_all(self);
+
+ client_manage(self->super.window, self);
+
+ self->mapped = TRUE;
+}
+
+void prompt_hide(ObPrompt *self)
+{
+ XUnmapWindow(obt_display, self->super.window);
+ self->mapped = FALSE;
+}
+
+gboolean prompt_key_event(ObPrompt *self, XEvent *e)
+{
+ gboolean shift;
+ guint shift_mask, mods;
+ KeySym sym;
+
+ if (e->type != KeyPress) return FALSE;
+
+ shift_mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT);
+ mods = obt_keyboard_only_modmasks(e->xkey.state);
+ shift = !!(mods & shift_mask);
+
+ /* only accept shift */
+ if (mods != 0 && mods != shift_mask)
+ return FALSE;
+
+ sym = obt_keyboard_keypress_to_keysym(e);
+
+ if (sym == XK_Escape)
+ prompt_cancel(self);
+ else if (sym == XK_Return || sym == XK_KP_Enter || sym == XK_space)
+ prompt_run_callback(self, self->focus->result);
+ else if (sym == XK_Tab || sym == XK_Left || sym == XK_Right) {
+ gint i;
+ gboolean left;
+ ObPromptElement *oldfocus;
+
+ left = (sym == XK_Left) || ((sym == XK_Tab) && shift);
+ oldfocus = self->focus;
+
+ for (i = 0; i < self->n_buttons; ++i)
+ if (self->focus == &self->button[i]) break;
+ i += (left ? -1 : 1);
+ if (i < 0) i = self->n_buttons - 1;
+ else if (i >= self->n_buttons) i = 0;
+ self->focus = &self->button[i];
+
+ if (oldfocus != self->focus) render_button(self, oldfocus);
+ render_button(self, self->focus);
+ }
+ return TRUE;
+}
+
+gboolean prompt_mouse_event(ObPrompt *self, XEvent *e)
+{
+ gint i;
+ ObPromptElement *but;
+
+ if (e->type != ButtonPress && e->type != ButtonRelease &&
+ e->type != MotionNotify) return FALSE;
+
+ /* find the button */
+ but = NULL;
+ for (i = 0; i < self->n_buttons; ++i)
+ if (self->button[i].window ==
+ (e->type == MotionNotify ? e->xmotion.window : e->xbutton.window))
+ {
+ but = &self->button[i];
+ break;
+ }
+ if (!but) return FALSE;
+
+ if (e->type == ButtonPress) {
+ ObPromptElement *oldfocus;
+
+ oldfocus = self->focus;
+
+ but->pressed = but->hover = TRUE;
+ self->focus = but;
+
+ if (oldfocus != but) render_button(self, oldfocus);
+ render_button(self, but);
+ }
+ else if (e->type == ButtonRelease) {
+ if (but->hover)
+ prompt_run_callback(self, but->result);
+ but->pressed = FALSE;
+ }
+ else if (e->type == MotionNotify) {
+ if (but->pressed) {
+ gboolean hover;
+
+ hover = (e->xmotion.x >= 0 && e->xmotion.y >= 0 &&
+ e->xmotion.x < but->width && e->xmotion.y < but->height);
+
+ if (hover != but->hover) {
+ but->hover = hover;
+ render_button(self, but);
+ }
+ }
+ }
+ return TRUE;
+}
+
+void prompt_cancel(ObPrompt *self)
+{
+ prompt_run_callback(self, self->cancel_result);
+}
+
+static gboolean prompt_show_message_cb(ObPrompt *p, int res, gpointer data)
+{
+ return TRUE; /* call the cleanup func */
+}
+
+static void prompt_show_message_cleanup(ObPrompt *p, gpointer data)
+{
+ prompt_unref(p);
+}
+
+ObPrompt* prompt_show_message(const gchar *msg, const gchar *title,
+ const gchar *answer)
+{
+ ObPrompt *p;
+ ObPromptAnswer ans[] = {
+ { answer, 0 }
+ };
+
+ p = prompt_new(msg, title, ans, 1, 0, 0,
+ prompt_show_message_cb, prompt_show_message_cleanup, NULL);
+ prompt_show(p, NULL, FALSE);
+ return p;
+}
+
+static void prompt_run_callback(ObPrompt *self, gint result)
+{
+ prompt_ref(self);
+ if (self->func) {
+ gboolean clean = self->func(self, self->focus->result, self->data);
+ if (clean && self->cleanup)
+ self->cleanup(self, self->data);
+ }
+ prompt_hide(self);
+ prompt_unref(self);
+}
diff --git a/openbox/prompt.h b/openbox/prompt.h
new file mode 100644
index 0000000..1358574
--- /dev/null
+++ b/openbox/prompt.h
@@ -0,0 +1,125 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ prompt.h for the Openbox window manager
+ Copyright (c) 2008 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef ob__prompt_h
+#define ob__prompt_h
+
+#include "window.h"
+#include "geom.h"
+#include "obrender/render.h"
+#include "obt/keyboard.h"
+#include <glib.h>
+#include <X11/Xlib.h>
+
+typedef struct _ObPrompt ObPrompt;
+typedef struct _ObPromptElement ObPromptElement;
+typedef struct _ObPromptAnswer ObPromptAnswer;
+
+typedef gboolean (*ObPromptCallback)(ObPrompt *p, gint result, gpointer data);
+typedef void (*ObPromptCleanup)(ObPrompt *p, gpointer data);
+
+struct _ObPromptElement {
+ gchar *text;
+ Window window;
+
+ gint x, y, width, height;
+ gboolean pressed;
+ gboolean hover;
+ gint result;
+};
+
+struct _ObPrompt
+{
+ ObInternalWindow super;
+ gint ref;
+
+ ObtIC *ic;
+ guint event_mask;
+
+ /* keep a copy of this because we re-render things that may need it
+ (i.e. the buttons) */
+ RrAppearance *a_bg;
+
+ gboolean mapped;
+ gint width, height;
+ gint msg_wbound;
+
+ ObPromptElement msg;
+
+ /* one for each answer */
+ ObPromptElement *button;
+ gint n_buttons;
+
+ /* points to the button with the focus */
+ ObPromptElement *focus;
+ /* the default button to have selected */
+ gint default_result;
+ /* the cancel result if the dialog is closed */
+ gint cancel_result;
+
+ ObPromptCallback func;
+ ObPromptCleanup cleanup;
+ gpointer data;
+};
+
+struct _ObPromptAnswer {
+ const gchar *text;
+ gint result;
+};
+
+void prompt_startup(gboolean reconfig);
+void prompt_shutdown(gboolean reconfig);
+
+/*! Create a new prompt
+ @param answers A number of ObPromptAnswers which define the buttons which
+ will appear in the dialog from left to right, and the result
+ returned when they are selected.
+ @param n_answers The number of answers
+ @param default_result The result for the answer button selected by default
+ @param cancel_result The result that is given if the dialog is closed instead
+ of having a button presssed
+ @param func The callback function which is called when the dialog is closed
+ or a button is pressed
+ @param cleanup The cleanup function which is called if the prompt system
+ is shutting down, and someone is still holding a reference to the
+ prompt. This callback should cause the prompt's refcount to go to
+ zero so it can be freed, and free any other memory associated with
+ the prompt. The cleanup function is also called if the prompt's
+ callback function returns TRUE.
+ @param data User defined data which will be passed to the callback
+*/
+ObPrompt* prompt_new(const gchar *msg, const gchar *title,
+ const ObPromptAnswer *answers, gint n_answers,
+ gint default_result, gint cancel_result,
+ ObPromptCallback func, ObPromptCleanup cleanup,
+ gpointer data);
+void prompt_ref(ObPrompt *self);
+void prompt_unref(ObPrompt *self);
+
+/*! Show the prompt. It will be centered within the given area rectangle */
+void prompt_show(ObPrompt *self, struct _ObClient *parent, gboolean modal);
+void prompt_hide(ObPrompt *self);
+
+gboolean prompt_key_event(ObPrompt *self, XEvent *e);
+gboolean prompt_mouse_event(ObPrompt *self, XEvent *e);
+void prompt_cancel(ObPrompt *self);
+
+ObPrompt* prompt_show_message(const gchar *msg, const gchar *title,
+ const gchar *answer);
+
+#endif
diff --git a/openbox/resist.c b/openbox/resist.c
new file mode 100644
index 0000000..12f5632
--- /dev/null
+++ b/openbox/resist.c
@@ -0,0 +1,416 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ resist.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "resist.h"
+#include "client.h"
+#include "frame.h"
+#include "stacking.h"
+#include "screen.h"
+#include "dock.h"
+#include "config.h"
+
+#include <glib.h>
+
+static gboolean resist_move_window(Rect window,
+ Rect target, gint resist,
+ gint *x, gint *y)
+{
+ gint l, t, r, b; /* requested edges */
+ gint cl, ct, cr, cb; /* current edges */
+ gint w, h; /* current size */
+ gint tl, tt, tr, tb; /* 1 past the target's edges on each side */
+ gboolean snapx = 0, snapy = 0;
+
+ w = window.width;
+ h = window.height;
+
+ l = *x;
+ t = *y;
+ r = l + w - 1;
+ b = t + h - 1;
+
+ cl = RECT_LEFT(window);
+ ct = RECT_TOP(window);
+ cr = RECT_RIGHT(window);
+ cb = RECT_BOTTOM(window);
+
+ tl = RECT_LEFT(target) - 1;
+ tt = RECT_TOP(target) - 1;
+ tr = RECT_RIGHT(target) + 1;
+ tb = RECT_BOTTOM(target) + 1;
+
+ /* snapx and snapy ensure that the window snaps to the top-most
+ window edge available, without going all the way from
+ bottom-to-top in the stacking list
+ */
+ if (!snapx) {
+ if (ct < tb && cb > tt) {
+ if (cl >= tr && l < tr && l >= tr - resist)
+ *x = tr, snapx = TRUE;
+ else if (cr <= tl && r > tl &&
+ r <= tl + resist)
+ *x = tl - w + 1, snapx = TRUE;
+ if (snapx) {
+ /* try to corner snap to the window */
+ if (ct > tt && t <= tt &&
+ t > tt - resist)
+ *y = tt + 1, snapy = TRUE;
+ else if (cb < tb && b >= tb &&
+ b < tb + resist)
+ *y = tb - h, snapy = TRUE;
+ }
+ }
+ }
+ if (!snapy) {
+ if (cl < tr && cr > tl) {
+ if (ct >= tb && t < tb && t >= tb - resist)
+ *y = tb, snapy = TRUE;
+ else if (cb <= tt && b > tt &&
+ b <= tt + resist)
+ *y = tt - h + 1, snapy = TRUE;
+ if (snapy) {
+ /* try to corner snap to the window */
+ if (cl > tl && l <= tl &&
+ l > tl - resist)
+ *x = tl + 1, snapx = TRUE;
+ else if (cr < tr && r >= tr &&
+ r < tr + resist)
+ *x = tr - w, snapx = TRUE;
+ }
+ }
+ }
+
+ return snapx && snapy;
+}
+
+void resist_move_windows(ObClient *c, gint resist, gint *x, gint *y)
+{
+ GList *it;
+ Rect dock_area;
+
+ if (!resist) return;
+
+ frame_client_gravity(c->frame, x, y);
+
+
+ for (it = stacking_list; it; it = g_list_next(it)) {
+ ObClient *target;
+
+ if (!WINDOW_IS_CLIENT(it->data))
+ continue;
+ target = it->data;
+
+ /* don't snap to self or non-visibles */
+ if (!target->frame->visible || target == c)
+ continue;
+ /* don't snap to windows set to below and skip_taskbar (desklets) */
+ if (target->below && !c->below && target->skip_taskbar)
+ continue;
+
+ if (resist_move_window(c->frame->area, target->frame->area,
+ resist, x, y))
+ break;
+ }
+ dock_get_area(&dock_area);
+ resist_move_window(c->frame->area, dock_area, resist, x, y);
+
+ frame_frame_gravity(c->frame, x, y);
+}
+
+void resist_move_monitors(ObClient *c, gint resist, gint *x, gint *y)
+{
+ Rect *area;
+ const Rect *parea;
+ guint i;
+ gint l, t, r, b; /* requested edges */
+ gint al, at, ar, ab; /* screen area edges */
+ gint pl, pt, pr, pb; /* physical screen area edges */
+ gint cl, ct, cr, cb; /* current edges */
+ gint w, h; /* current size */
+ Rect desired_area;
+
+ if (!resist) return;
+
+ frame_client_gravity(c->frame, x, y);
+
+ w = c->frame->area.width;
+ h = c->frame->area.height;
+
+ l = *x;
+ t = *y;
+ r = l + w - 1;
+ b = t + h - 1;
+
+ cl = RECT_LEFT(c->frame->area);
+ ct = RECT_TOP(c->frame->area);
+ cr = RECT_RIGHT(c->frame->area);
+ cb = RECT_BOTTOM(c->frame->area);
+
+ RECT_SET(desired_area, c->frame->area.x, c->frame->area.y,
+ c->frame->area.width, c->frame->area.height);
+
+ for (i = 0; i < screen_num_monitors; ++i) {
+ parea = screen_physical_area_monitor(i);
+
+ if (!RECT_INTERSECTS_RECT(*parea, c->frame->area))
+ continue;
+
+ area = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS,
+ &desired_area);
+
+ al = RECT_LEFT(*area);
+ at = RECT_TOP(*area);
+ ar = RECT_RIGHT(*area);
+ ab = RECT_BOTTOM(*area);
+ pl = RECT_LEFT(*parea);
+ pt = RECT_TOP(*parea);
+ pr = RECT_RIGHT(*parea);
+ pb = RECT_BOTTOM(*parea);
+
+ if (cl >= al && l < al && l >= al - resist)
+ *x = al;
+ else if (cr <= ar && r > ar && r <= ar + resist)
+ *x = ar - w + 1;
+ else if (cl >= pl && l < pl && l >= pl - resist)
+ *x = pl;
+ else if (cr <= pr && r > pr && r <= pr + resist)
+ *x = pr - w + 1;
+
+ if (ct >= at && t < at && t >= at - resist)
+ *y = at;
+ else if (cb <= ab && b > ab && b < ab + resist)
+ *y = ab - h + 1;
+ else if (ct >= pt && t < pt && t >= pt - resist)
+ *y = pt;
+ else if (cb <= pb && b > pb && b < pb + resist)
+ *y = pb - h + 1;
+
+ g_slice_free(Rect, area);
+ }
+
+ frame_frame_gravity(c->frame, x, y);
+}
+
+static gboolean resist_size_window(Rect window, Rect target, gint resist,
+ gint *w, gint *h, ObDirection dir)
+{
+ gint l, t, r, b; /* my left, top, right and bottom sides */
+ gint tl, tt, tr, tb; /* target's left, top, right and bottom bottom sides*/
+ gint dlt, drb; /* my destination left/top and right/bottom sides */
+ gboolean snapx = 0, snapy = 0;
+ gint orgw, orgh;
+
+ l = RECT_LEFT(window);
+ t = RECT_TOP(window);
+ r = RECT_RIGHT(window);
+ b = RECT_BOTTOM(window);
+
+ orgw = window.width;
+ orgh = window.height;
+
+ tl = RECT_LEFT(target);
+ tt = RECT_TOP(target);
+ tr = RECT_RIGHT(target);
+ tb = RECT_BOTTOM(target);
+
+ if (!snapx) {
+ /* horizontal snapping */
+ if (t < tb && b > tt) {
+ switch (dir) {
+ case OB_DIRECTION_EAST:
+ case OB_DIRECTION_NORTHEAST:
+ case OB_DIRECTION_SOUTHEAST:
+ case OB_DIRECTION_NORTH:
+ case OB_DIRECTION_SOUTH:
+ dlt = l;
+ drb = r + *w - orgw;
+ if (r < tl && drb >= tl &&
+ drb < tl + resist)
+ *w = tl - l, snapx = TRUE;
+ break;
+ case OB_DIRECTION_WEST:
+ case OB_DIRECTION_NORTHWEST:
+ case OB_DIRECTION_SOUTHWEST:
+ dlt = l - *w + orgw;
+ drb = r;
+ if (l > tr && dlt <= tr &&
+ dlt > tr - resist)
+ *w = r - tr, snapx = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!snapy) {
+ /* vertical snapping */
+ if (l < tr && r > tl) {
+ switch (dir) {
+ case OB_DIRECTION_SOUTH:
+ case OB_DIRECTION_SOUTHWEST:
+ case OB_DIRECTION_SOUTHEAST:
+ case OB_DIRECTION_EAST:
+ case OB_DIRECTION_WEST:
+ dlt = t;
+ drb = b + *h - orgh;
+ if (b < tt && drb >= tt &&
+ drb < tt + resist)
+ *h = tt - t, snapy = TRUE;
+ break;
+ case OB_DIRECTION_NORTH:
+ case OB_DIRECTION_NORTHWEST:
+ case OB_DIRECTION_NORTHEAST:
+ dlt = t - *h + orgh;
+ drb = b;
+ if (t > tb && dlt <= tb &&
+ dlt > tb - resist)
+ *h = b - tb, snapy = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* snapped both ways */
+ return snapx && snapy;
+}
+
+void resist_size_windows(ObClient *c, gint resist, gint *w, gint *h,
+ ObDirection dir)
+{
+ GList *it;
+ ObClient *target; /* target */
+ Rect dock_area;
+
+ if (!resist) return;
+
+ for (it = stacking_list; it; it = g_list_next(it)) {
+ if (!WINDOW_IS_CLIENT(it->data))
+ continue;
+ target = it->data;
+
+ /* don't snap to invisibles or ourself */
+ if (!target->frame->visible || target == c)
+ continue;
+ /* don't snap to windows set to below and skip_taskbar (desklets) */
+ if (target->below && !c->below && target->skip_taskbar)
+ continue;
+
+ if (resist_size_window(c->frame->area, target->frame->area,
+ resist, w, h, dir))
+ break;
+ }
+ dock_get_area(&dock_area);
+ resist_size_window(c->frame->area, dock_area,
+ resist, w, h, dir);
+}
+
+void resist_size_monitors(ObClient *c, gint resist, gint *w, gint *h,
+ ObDirection dir)
+{
+ gint l, t, r, b; /* my left, top, right and bottom sides */
+ gint dlt, drb; /* my destination left/top and right/bottom sides */
+ Rect *area;
+ const Rect *parea;
+ gint al, at, ar, ab; /* screen boundaries */
+ gint pl, pt, pr, pb; /* physical screen boundaries */
+ guint i;
+ Rect desired_area;
+
+ if (!resist) return;
+
+ l = RECT_LEFT(c->frame->area);
+ r = RECT_RIGHT(c->frame->area);
+ t = RECT_TOP(c->frame->area);
+ b = RECT_BOTTOM(c->frame->area);
+
+ RECT_SET(desired_area, c->area.x, c->area.y, *w, *h);
+
+ for (i = 0; i < screen_num_monitors; ++i) {
+ parea = screen_physical_area_monitor(i);
+
+ if (!RECT_INTERSECTS_RECT(*parea, c->frame->area))
+ continue;
+
+ area = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS,
+ &desired_area);
+
+ /* get the screen boundaries */
+ al = RECT_LEFT(*area);
+ at = RECT_TOP(*area);
+ ar = RECT_RIGHT(*area);
+ ab = RECT_BOTTOM(*area);
+ pl = RECT_LEFT(*parea);
+ pt = RECT_TOP(*parea);
+ pr = RECT_RIGHT(*parea);
+ pb = RECT_BOTTOM(*parea);
+
+ /* horizontal snapping */
+ switch (dir) {
+ case OB_DIRECTION_EAST:
+ case OB_DIRECTION_NORTHEAST:
+ case OB_DIRECTION_SOUTHEAST:
+ case OB_DIRECTION_NORTH:
+ case OB_DIRECTION_SOUTH:
+ dlt = l;
+ drb = r + *w - c->frame->area.width;
+ if (r <= ar && drb > ar && drb <= ar + resist)
+ *w = ar - l + 1;
+ else if (r <= pr && drb > pr && drb <= pr + resist)
+ *w = pr - l + 1;
+ break;
+ case OB_DIRECTION_WEST:
+ case OB_DIRECTION_NORTHWEST:
+ case OB_DIRECTION_SOUTHWEST:
+ dlt = l - *w + c->frame->area.width;
+ drb = r;
+ if (l >= al && dlt < al && dlt >= al - resist)
+ *w = r - al + 1;
+ else if (l >= pl && dlt < pl && dlt >= pl - resist)
+ *w = r - pl + 1;
+ break;
+ }
+
+ /* vertical snapping */
+ switch (dir) {
+ case OB_DIRECTION_SOUTH:
+ case OB_DIRECTION_SOUTHWEST:
+ case OB_DIRECTION_SOUTHEAST:
+ case OB_DIRECTION_WEST:
+ case OB_DIRECTION_EAST:
+ dlt = t;
+ drb = b + *h - c->frame->area.height;
+ if (b <= ab && drb > ab && drb <= ab + resist)
+ *h = ab - t + 1;
+ else if (b <= pb && drb > pb && drb <= pb + resist)
+ *h = pb - t + 1;
+ break;
+ case OB_DIRECTION_NORTH:
+ case OB_DIRECTION_NORTHWEST:
+ case OB_DIRECTION_NORTHEAST:
+ dlt = t - *h + c->frame->area.height;
+ drb = b;
+ if (t >= at && dlt < at && dlt >= at - resist)
+ *h = b - at + 1;
+ else if (t >= pt && dlt < pt && dlt >= pt - resist)
+ *h = b - pt + 1;
+ break;
+ }
+
+ g_slice_free(Rect, area);
+ }
+}
diff --git a/openbox/resist.h b/openbox/resist.h
new file mode 100644
index 0000000..31cc717
--- /dev/null
+++ b/openbox/resist.h
@@ -0,0 +1,41 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ resist.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef ob__resist_h
+#define ob__resist_h
+
+#include "misc.h"
+
+#include <glib.h>
+
+struct _ObClient;
+
+/*! @x The client's x destination (in the client's coordinates, not the frame's
+ @y The client's y destination (in the client's coordinates, not the frame's
+*/
+void resist_move_windows(struct _ObClient *c, gint resist, gint *x, gint *y);
+/*! @x The client's x destination (in the client's coordinates, not the frame's
+ @y The client's y destination (in the client's coordinates, not the frame's
+*/
+void resist_move_monitors(struct _ObClient *c, gint resist, gint *x, gint *y);
+void resist_size_windows(struct _ObClient *c, gint resist, gint *w, gint *h,
+ ObDirection dir);
+void resist_size_monitors(struct _ObClient *c, gint resist, gint *w, gint *h,
+ ObDirection dir);
+
+#endif
diff --git a/openbox/screen.c b/openbox/screen.c
new file mode 100644
index 0000000..ffe74a0
--- /dev/null
+++ b/openbox/screen.c
@@ -0,0 +1,1761 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ screen.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "debug.h"
+#include "openbox.h"
+#include "dock.h"
+#include "grab.h"
+#include "startupnotify.h"
+#include "moveresize.h"
+#include "config.h"
+#include "screen.h"
+#include "client.h"
+#include "session.h"
+#include "frame.h"
+#include "event.h"
+#include "focus.h"
+#include "focus_cycle.h"
+#include "popup.h"
+#include "version.h"
+#include "obrender/render.h"
+#include "gettext.h"
+#include "obt/display.h"
+#include "obt/xqueue.h"
+#include "obt/prop.h"
+
+#include <X11/Xlib.h>
+#ifdef HAVE_UNISTD_H
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+#include <assert.h>
+
+/*! The event mask to grab on the root window */
+#define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
+ EnterWindowMask | LeaveWindowMask | \
+ SubstructureRedirectMask | FocusChangeMask | \
+ ButtonPressMask | ButtonReleaseMask)
+
+static gboolean screen_validate_layout(ObDesktopLayout *l);
+static gboolean replace_wm(void);
+static void screen_tell_ksplash(void);
+static void screen_fallback_focus(void);
+
+guint screen_num_desktops;
+guint screen_num_monitors;
+guint screen_desktop;
+guint screen_last_desktop;
+gboolean screen_showing_desktop;
+ObDesktopLayout screen_desktop_layout;
+gchar **screen_desktop_names;
+Window screen_support_win;
+Time screen_desktop_user_time = CurrentTime;
+
+static Size screen_physical_size;
+static guint screen_old_desktop;
+static gboolean screen_desktop_timeout = TRUE;
+static guint screen_desktop_timer = 0;
+/*! An array of desktops, holding an array of areas per monitor */
+static Rect *monitor_area = NULL;
+/*! An array of desktops, holding an array of struts */
+static GSList *struts_top = NULL;
+static GSList *struts_left = NULL;
+static GSList *struts_right = NULL;
+static GSList *struts_bottom = NULL;
+
+static ObPagerPopup *desktop_popup;
+static guint desktop_popup_timer = 0;
+static gboolean desktop_popup_perm;
+
+/*! The number of microseconds that you need to be on a desktop before it will
+ replace the remembered "last desktop" */
+#define REMEMBER_LAST_DESKTOP_TIME 750
+
+static gboolean replace_wm(void)
+{
+ gchar *wm_sn;
+ Atom wm_sn_atom;
+ Window current_wm_sn_owner;
+ Time timestamp;
+
+ wm_sn = g_strdup_printf("WM_S%d", ob_screen);
+ wm_sn_atom = XInternAtom(obt_display, wm_sn, FALSE);
+ g_free(wm_sn);
+
+ current_wm_sn_owner = XGetSelectionOwner(obt_display, wm_sn_atom);
+ if (current_wm_sn_owner == screen_support_win)
+ current_wm_sn_owner = None;
+ if (current_wm_sn_owner) {
+ if (!ob_replace_wm) {
+ g_message(_("A window manager is already running on screen %d"),
+ ob_screen);
+ return FALSE;
+ }
+ obt_display_ignore_errors(TRUE);
+
+ /* We want to find out when the current selection owner dies */
+ XSelectInput(obt_display, current_wm_sn_owner, StructureNotifyMask);
+ XSync(obt_display, FALSE);
+
+ obt_display_ignore_errors(FALSE);
+ if (obt_display_error_occured)
+ current_wm_sn_owner = None;
+ }
+
+ timestamp = event_time();
+
+ XSetSelectionOwner(obt_display, wm_sn_atom, screen_support_win,
+ timestamp);
+
+ if (XGetSelectionOwner(obt_display, wm_sn_atom) != screen_support_win) {
+ g_message(_("Could not acquire window manager selection on screen %d"),
+ ob_screen);
+ return FALSE;
+ }
+
+ /* Wait for old window manager to go away */
+ if (current_wm_sn_owner) {
+ gulong wait = 0;
+ const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
+ ObtXQueueWindowType wt;
+
+ wt.window = current_wm_sn_owner;
+ wt.type = DestroyNotify;
+
+ while (wait < timeout) {
+ /* Checks the local queue and incoming events for this event */
+ if (xqueue_exists_local(xqueue_match_window_type, &wt))
+ break;
+ g_usleep(G_USEC_PER_SEC / 10);
+ wait += G_USEC_PER_SEC / 10;
+ }
+
+ if (wait >= timeout) {
+ g_message(_("The WM on screen %d is not exiting"), ob_screen);
+ return FALSE;
+ }
+ }
+
+ /* Send client message indicating that we are now the WM */
+ obt_prop_message(ob_screen, obt_root(ob_screen), OBT_PROP_ATOM(MANAGER),
+ timestamp, wm_sn_atom, screen_support_win, 0, 0,
+ SubstructureNotifyMask);
+
+ return TRUE;
+}
+
+gboolean screen_annex(void)
+{
+ XSetWindowAttributes attrib;
+ pid_t pid;
+ gint i, num_support;
+ gulong *supported;
+
+ /* create the netwm support window */
+ attrib.override_redirect = TRUE;
+ attrib.event_mask = PropertyChangeMask;
+ screen_support_win = XCreateWindow(obt_display, obt_root(ob_screen),
+ -100, -100, 1, 1, 0,
+ CopyFromParent, InputOutput,
+ CopyFromParent,
+ CWEventMask | CWOverrideRedirect,
+ &attrib);
+ XMapWindow(obt_display, screen_support_win);
+ XLowerWindow(obt_display, screen_support_win);
+
+ if (!replace_wm()) {
+ XDestroyWindow(obt_display, screen_support_win);
+ return FALSE;
+ }
+
+ obt_display_ignore_errors(TRUE);
+ XSelectInput(obt_display, obt_root(ob_screen), ROOT_EVENTMASK);
+ obt_display_ignore_errors(FALSE);
+ if (obt_display_error_occured) {
+ g_message(_("A window manager is already running on screen %d"),
+ ob_screen);
+
+ XDestroyWindow(obt_display, screen_support_win);
+ return FALSE;
+ }
+
+ screen_set_root_cursor();
+
+ /* set the OPENBOX_PID hint */
+ pid = getpid();
+ OBT_PROP_SET32(obt_root(ob_screen), OPENBOX_PID, CARDINAL, pid);
+
+ /* set supporting window */
+ OBT_PROP_SET32(obt_root(ob_screen),
+ NET_SUPPORTING_WM_CHECK, WINDOW, screen_support_win);
+
+ /* set properties on the supporting window */
+ OBT_PROP_SETS(screen_support_win, NET_WM_NAME, "Openbox");
+ OBT_PROP_SET32(screen_support_win, NET_SUPPORTING_WM_CHECK,
+ WINDOW, screen_support_win);
+
+ /* set the _NET_SUPPORTED_ATOMS hint */
+
+ /* this is all the atoms after NET_SUPPORTED in the ObtPropAtoms enum */
+ num_support = OBT_PROP_NUM_ATOMS - OBT_PROP_NET_SUPPORTED - 1;
+ i = 0;
+ supported = g_new(gulong, num_support);
+ supported[i++] = OBT_PROP_ATOM(NET_SUPPORTING_WM_CHECK);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_FULL_PLACEMENT);
+ supported[i++] = OBT_PROP_ATOM(NET_CURRENT_DESKTOP);
+ supported[i++] = OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS);
+ supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_GEOMETRY);
+ supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_VIEWPORT);
+ supported[i++] = OBT_PROP_ATOM(NET_ACTIVE_WINDOW);
+ supported[i++] = OBT_PROP_ATOM(NET_WORKAREA);
+ supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST);
+ supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST_STACKING);
+ supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_NAMES);
+ supported[i++] = OBT_PROP_ATOM(NET_CLOSE_WINDOW);
+ supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_LAYOUT);
+ supported[i++] = OBT_PROP_ATOM(NET_SHOWING_DESKTOP);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_NAME);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_NAME);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_NAME);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_ICON_NAME);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_DESKTOP);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ICON);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ALLOWED_ACTIONS);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_OPACITY);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STATE);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
+ supported[i++] = OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_MOVERESIZE);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME);
+/*
+ supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME_WINDOW);
+*/
+ supported[i++] = OBT_PROP_ATOM(NET_FRAME_EXTENTS);
+ supported[i++] = OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS);
+ supported[i++] = OBT_PROP_ATOM(NET_RESTACK_WINDOW);
+ supported[i++] = OBT_PROP_ATOM(NET_STARTUP_ID);
+#ifdef SYNC
+ supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER);
+#endif
+ supported[i++] = OBT_PROP_ATOM(NET_WM_PID);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_PING);
+
+ supported[i++] = OBT_PROP_ATOM(KDE_WM_CHANGE_STATE);
+ supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_FRAME_STRUT);
+ supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE);
+
+ supported[i++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
+ supported[i++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
+ supported[i++] = OBT_PROP_ATOM(OPENBOX_PID);
+ supported[i++] = OBT_PROP_ATOM(OB_THEME);
+ supported[i++] = OBT_PROP_ATOM(OB_CONFIG_FILE);
+ supported[i++] = OBT_PROP_ATOM(OB_CONTROL);
+ supported[i++] = OBT_PROP_ATOM(OB_VERSION);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_ROLE);
+ 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_TYPE);
+ g_assert(i == num_support);
+
+ OBT_PROP_SETA32(obt_root(ob_screen),
+ NET_SUPPORTED, ATOM, supported, num_support);
+ g_free(supported);
+
+ OBT_PROP_SETS(RootWindow(obt_display, ob_screen), OB_VERSION,
+ OPENBOX_VERSION);
+
+ screen_tell_ksplash();
+
+ return TRUE;
+}
+
+static void screen_tell_ksplash(void)
+{
+ XEvent e;
+ char **argv;
+
+ argv = g_new(gchar*, 6);
+ argv[0] = g_strdup("dcop");
+ argv[1] = g_strdup("ksplash");
+ argv[2] = g_strdup("ksplash");
+ argv[3] = g_strdup("upAndRunning(QString)");
+ argv[4] = g_strdup("wm started");
+ argv[5] = NULL;
+
+ /* tell ksplash through the dcop server command line interface */
+ g_spawn_async(NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD |
+ G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL,
+ NULL, NULL, NULL, NULL);
+ g_strfreev(argv);
+
+ /* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to
+ hear it anyways. perhaps it is for old ksplash. or new ksplash. or
+ something. oh well. */
+ e.xclient.type = ClientMessage;
+ e.xclient.display = obt_display;
+ e.xclient.window = obt_root(ob_screen);
+ e.xclient.message_type =
+ XInternAtom(obt_display, "_KDE_SPLASH_PROGRESS", False);
+ e.xclient.format = 8;
+ strcpy(e.xclient.data.b, "wm started");
+ XSendEvent(obt_display, obt_root(ob_screen),
+ False, SubstructureNotifyMask, &e);
+}
+
+void screen_startup(gboolean reconfig)
+{
+ gchar **names = NULL;
+ guint32 d;
+ gboolean namesexist = FALSE;
+
+ desktop_popup = pager_popup_new();
+ desktop_popup_perm = FALSE;
+ pager_popup_height(desktop_popup, POPUP_HEIGHT);
+
+ if (reconfig) {
+ /* update the pager popup's width */
+ pager_popup_text_width_to_strings(desktop_popup,
+ screen_desktop_names,
+ screen_num_desktops);
+ return;
+ }
+
+ /* get the initial size */
+ screen_resize();
+
+ /* have names already been set for the desktops? */
+ if (OBT_PROP_GETSS_UTF8(obt_root(ob_screen), NET_DESKTOP_NAMES, &names)) {
+ g_strfreev(names);
+ namesexist = TRUE;
+ }
+
+ /* if names don't exist and we have session names, set those.
+ do this stuff BEFORE setting the number of desktops, because that
+ will create default names for them
+ */
+ if (!namesexist && session_desktop_names != NULL) {
+ guint i, numnames;
+ GSList *it;
+
+ /* get the desktop names */
+ numnames = g_slist_length(session_desktop_names);
+ names = g_new(gchar*, numnames + 1);
+ names[numnames] = NULL;
+ for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
+ names[i] = g_strdup(it->data);
+
+ /* set the root window property */
+ OBT_PROP_SETSS(obt_root(ob_screen),
+ NET_DESKTOP_NAMES, (const gchar*const*)names);
+
+ g_strfreev(names);
+ }
+
+ /* set the number of desktops, if it's not already set.
+
+ this will also set the default names from the config file up for
+ desktops that don't have names yet */
+ screen_num_desktops = 0;
+ if (OBT_PROP_GET32(obt_root(ob_screen),
+ NET_NUMBER_OF_DESKTOPS, CARDINAL, &d))
+ {
+ if (d != config_desktops_num) {
+ /* TRANSLATORS: If you need to specify a different order of the
+ arguments, you can use %1$d for the first one and %2$d for the
+ second one. For example,
+ "The current session has %2$d desktops, but Openbox is configured for %1$d ..." */
+ g_warning(ngettext("Openbox is configured for %d desktop, but the current session has %d. Overriding the Openbox configuration.", "Openbox is configured for %d desktops, but the current session has %d. Overriding the Openbox configuration.", config_desktops_num),
+ config_desktops_num, d);
+ }
+ screen_set_num_desktops(d);
+ }
+ /* restore from session if possible */
+ else if (session_num_desktops)
+ screen_set_num_desktops(session_num_desktops);
+ else
+ screen_set_num_desktops(config_desktops_num);
+
+ screen_desktop = screen_num_desktops; /* something invalid */
+ /* start on the current desktop when a wm was already running */
+ if (OBT_PROP_GET32(obt_root(ob_screen),
+ NET_CURRENT_DESKTOP, CARDINAL, &d) &&
+ d < screen_num_desktops)
+ {
+ screen_set_desktop(d, FALSE);
+ } else if (session_desktop >= 0)
+ screen_set_desktop(MIN((guint)session_desktop,
+ screen_num_desktops), FALSE);
+ else
+ screen_set_desktop(MIN(config_screen_firstdesk,
+ screen_num_desktops) - 1, FALSE);
+ screen_last_desktop = screen_desktop;
+
+ /* don't start in showing-desktop mode */
+ screen_showing_desktop = FALSE;
+ OBT_PROP_SET32(obt_root(ob_screen),
+ NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop);
+
+ if (session_desktop_layout_present &&
+ screen_validate_layout(&session_desktop_layout))
+ {
+ screen_desktop_layout = session_desktop_layout;
+ }
+ else
+ screen_update_layout();
+}
+
+void screen_shutdown(gboolean reconfig)
+{
+ pager_popup_free(desktop_popup);
+
+ if (reconfig)
+ return;
+
+ XSelectInput(obt_display, obt_root(ob_screen), NoEventMask);
+
+ /* we're not running here no more! */
+ OBT_PROP_ERASE(obt_root(ob_screen), OPENBOX_PID);
+ /* not without us */
+ OBT_PROP_ERASE(obt_root(ob_screen), NET_SUPPORTED);
+ /* don't keep this mode */
+ OBT_PROP_ERASE(obt_root(ob_screen), NET_SHOWING_DESKTOP);
+
+ XDestroyWindow(obt_display, screen_support_win);
+
+ g_strfreev(screen_desktop_names);
+ screen_desktop_names = NULL;
+}
+
+void screen_resize(void)
+{
+ gint w, h;
+ GList *it;
+ gulong geometry[2];
+
+ w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
+ h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
+
+ /* Set the _NET_DESKTOP_GEOMETRY hint */
+ screen_physical_size.width = geometry[0] = w;
+ screen_physical_size.height = geometry[1] = h;
+ OBT_PROP_SETA32(obt_root(ob_screen),
+ NET_DESKTOP_GEOMETRY, CARDINAL, geometry, 2);
+
+ if (ob_state() != OB_STATE_RUNNING)
+ return;
+
+ /* this calls screen_update_areas(), which we need ! */
+ dock_configure();
+
+ for (it = client_list; it; it = g_list_next(it)) {
+ client_move_onscreen(it->data, FALSE);
+ client_reconfigure(it->data, FALSE);
+ }
+}
+
+void screen_set_num_desktops(guint num)
+{
+ gulong *viewport;
+ GList *it, *stacking_copy;
+
+ g_assert(num > 0);
+
+ if (screen_num_desktops == num) return;
+
+ screen_num_desktops = num;
+ OBT_PROP_SET32(obt_root(ob_screen), NET_NUMBER_OF_DESKTOPS, CARDINAL, num);
+
+ /* set the viewport hint */
+ viewport = g_new0(gulong, num * 2);
+ OBT_PROP_SETA32(obt_root(ob_screen),
+ NET_DESKTOP_VIEWPORT, CARDINAL, viewport, num * 2);
+ g_free(viewport);
+
+ /* the number of rows/columns will differ */
+ screen_update_layout();
+
+ /* move windows on desktops that will no longer exist!
+ make a copy of the list cuz we're changing it */
+ stacking_copy = g_list_copy(stacking_list);
+ for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
+ if (WINDOW_IS_CLIENT(it->data)) {
+ ObClient *c = it->data;
+ if (c->desktop != DESKTOP_ALL && c->desktop >= num)
+ client_set_desktop(c, num - 1, FALSE, TRUE);
+ /* raise all the windows that are on the current desktop which
+ is being merged */
+ else if (screen_desktop == num - 1 &&
+ (c->desktop == DESKTOP_ALL ||
+ c->desktop == screen_desktop))
+ stacking_raise(CLIENT_AS_WINDOW(c));
+ }
+ }
+ g_list_free(stacking_copy);
+
+ /* change our struts/area to match (after moving windows) */
+ screen_update_areas();
+
+ /* may be some unnamed desktops that we need to fill in with names
+ (after updating the areas so the popup can resize) */
+ screen_update_desktop_names();
+
+ /* change our desktop if we're on one that no longer exists! */
+ if (screen_desktop >= screen_num_desktops)
+ screen_set_desktop(num - 1, TRUE);
+}
+
+static void screen_fallback_focus(void)
+{
+ ObClient *c;
+ gboolean allow_omni;
+
+ /* only allow omnipresent windows to get focus on desktop change if
+ an omnipresent window is already focused (it'll keep focus probably, but
+ maybe not depending on mouse-focus options) */
+ allow_omni = focus_client && (client_normal(focus_client) &&
+ focus_client->desktop == DESKTOP_ALL);
+
+ /* the client moved there already so don't move focus. prevent flicker
+ on sendtodesktop + follow */
+ if (focus_client && focus_client->desktop == screen_desktop)
+ return;
+
+ /* have to try focus here because when you leave an empty desktop
+ there is no focus out to watch for. also, we have different rules
+ here. we always allow it to look under the mouse pointer if
+ config_focus_last is FALSE
+
+ do this before hiding the windows so if helper windows are coming
+ with us, they don't get hidden
+ */
+ if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni,
+ !allow_omni)))
+ {
+ /* only do the flicker reducing stuff ahead of time if we are going
+ to call xsetinputfocus on the window ourselves. otherwise there is
+ no guarantee the window will actually take focus.. */
+ if (c->can_focus) {
+ /* reduce flicker by hiliting now rather than waiting for the
+ server FocusIn event */
+ frame_adjust_focus(c->frame, TRUE);
+ /* do this here so that if you switch desktops to a window with
+ helper windows then the helper windows won't flash */
+ client_bring_helper_windows(c);
+ }
+ }
+}
+
+static gboolean last_desktop_func(gpointer data)
+{
+ screen_desktop_timeout = TRUE;
+ screen_desktop_timer = 0;
+ return FALSE; /* don't repeat */
+}
+
+void screen_set_desktop(guint num, gboolean dofocus)
+{
+ GList *it;
+ guint previous;
+ gulong ignore_start;
+
+ g_assert(num < screen_num_desktops);
+
+ previous = screen_desktop;
+ screen_desktop = num;
+
+ if (previous == num) return;
+
+ OBT_PROP_SET32(obt_root(ob_screen), NET_CURRENT_DESKTOP, CARDINAL, num);
+
+ /* This whole thing decides when/how to save the screen_last_desktop so
+ that it can be restored later if you want */
+ if (screen_desktop_timeout) {
+ /* If screen_desktop_timeout is true, then we've been on this desktop
+ long enough and we can save it as the last desktop. */
+
+ if (screen_last_desktop == previous)
+ /* this is the startup state only */
+ screen_old_desktop = screen_desktop;
+ else {
+ /* save the "last desktop" as the "old desktop" */
+ screen_old_desktop = screen_last_desktop;
+ /* save the desktop we're coming from as the "last desktop" */
+ screen_last_desktop = previous;
+ }
+ }
+ else {
+ /* If screen_desktop_timeout is false, then we just got to this desktop
+ and we are moving away again. */
+
+ if (screen_desktop == screen_last_desktop) {
+ /* If we are moving to the "last desktop" .. */
+ if (previous == screen_old_desktop) {
+ /* .. from the "old desktop", change the last desktop to
+ be where we are coming from */
+ screen_last_desktop = screen_old_desktop;
+ }
+ else if (screen_last_desktop == screen_old_desktop) {
+ /* .. and also to the "old desktop", change the "last
+ desktop" to be where we are coming from */
+ screen_last_desktop = previous;
+ }
+ else {
+ /* .. from some other desktop, then set the "last desktop" to
+ be the saved "old desktop", i.e. where we were before the
+ "last desktop" */
+ screen_last_desktop = screen_old_desktop;
+ }
+ }
+ else {
+ /* If we are moving to any desktop besides the "last desktop"..
+ (this is the normal case) */
+ if (screen_desktop == screen_old_desktop) {
+ /* If moving to the "old desktop", which is not the
+ "last desktop", don't save anything */
+ }
+ else if (previous == screen_old_desktop) {
+ /* If moving from the "old desktop", and not to the
+ "last desktop", don't save anything */
+ }
+ else if (screen_last_desktop == screen_old_desktop) {
+ /* If the "last desktop" is the same as "old desktop" and
+ you're not moving to the "last desktop" then save where
+ we're coming from as the "last desktop" */
+ screen_last_desktop = previous;
+ }
+ else {
+ /* If the "last desktop" is different from the "old desktop"
+ and you're not moving to the "last desktop", then don't save
+ anything */
+ }
+ }
+ }
+ screen_desktop_timeout = FALSE;
+ if (screen_desktop_timer) g_source_remove(screen_desktop_timer);
+ screen_desktop_timer = g_timeout_add(REMEMBER_LAST_DESKTOP_TIME,
+ last_desktop_func, NULL);
+
+ ob_debug("Moving to desktop %d", num+1);
+
+ if (ob_state() == OB_STATE_RUNNING)
+ screen_show_desktop_popup(screen_desktop, FALSE);
+
+ /* ignore enter events caused by the move */
+ ignore_start = event_start_ignore_all_enters();
+
+ if (moveresize_client)
+ client_set_desktop(moveresize_client, num, TRUE, FALSE);
+
+ /* show windows before hiding the rest to lessen the enter/leave events */
+
+ /* show windows from top to bottom */
+ for (it = stacking_list; it; it = g_list_next(it)) {
+ if (WINDOW_IS_CLIENT(it->data)) {
+ ObClient *c = it->data;
+ client_show(c);
+ }
+ }
+
+ if (dofocus) screen_fallback_focus();
+
+ /* hide windows from bottom to top */
+ for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
+ if (WINDOW_IS_CLIENT(it->data)) {
+ ObClient *c = it->data;
+ if (client_hide(c)) {
+ if (c == focus_client) {
+ /* c was focused and we didn't do fallback clearly so make
+ sure openbox doesnt still consider the window focused.
+ this happens when using NextWindow with allDesktops,
+ since it doesnt want to move focus on desktop change,
+ but the focus is not going to stay with the current
+ window, which has now disappeared.
+ only do this if the client was actually hidden,
+ otherwise it can keep focus. */
+ focus_set_client(NULL);
+ }
+ }
+ }
+ }
+
+ focus_cycle_addremove(NULL, TRUE);
+
+ event_end_ignore_all_enters(ignore_start);
+
+ if (event_source_time() != CurrentTime)
+ screen_desktop_user_time = event_source_time();
+}
+
+void screen_add_desktop(gboolean current)
+{
+ gulong ignore_start;
+
+ /* ignore enter events caused by this */
+ ignore_start = event_start_ignore_all_enters();
+
+ screen_set_num_desktops(screen_num_desktops+1);
+
+ /* move all the clients over */
+ if (current) {
+ GList *it;
+
+ for (it = client_list; it; it = g_list_next(it)) {
+ ObClient *c = it->data;
+ if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop &&
+ /* don't move direct children, they'll be moved with their
+ parent - which will have to be on the same desktop */
+ !client_direct_parent(c))
+ {
+ ob_debug("moving window %s", c->title);
+ client_set_desktop(c, c->desktop+1, FALSE, TRUE);
+ }
+ }
+ }
+
+ event_end_ignore_all_enters(ignore_start);
+}
+
+void screen_remove_desktop(gboolean current)
+{
+ guint rmdesktop, movedesktop;
+ GList *it, *stacking_copy;
+ gulong ignore_start;
+
+ if (screen_num_desktops <= 1) return;
+
+ /* ignore enter events caused by this */
+ ignore_start = event_start_ignore_all_enters();
+
+ /* what desktop are we removing and moving to? */
+ if (current)
+ rmdesktop = screen_desktop;
+ else
+ rmdesktop = screen_num_desktops - 1;
+ if (rmdesktop < screen_num_desktops - 1)
+ movedesktop = rmdesktop + 1;
+ else
+ movedesktop = rmdesktop;
+
+ /* make a copy of the list cuz we're changing it */
+ stacking_copy = g_list_copy(stacking_list);
+ for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
+ if (WINDOW_IS_CLIENT(it->data)) {
+ ObClient *c = it->data;
+ guint d = c->desktop;
+ if (d != DESKTOP_ALL && d >= movedesktop &&
+ /* don't move direct children, they'll be moved with their
+ parent - which will have to be on the same desktop */
+ !client_direct_parent(c))
+ {
+ ob_debug("moving window %s", c->title);
+ client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
+ }
+ /* raise all the windows that are on the current desktop which
+ is being merged */
+ if ((screen_desktop == rmdesktop - 1 ||
+ screen_desktop == rmdesktop) &&
+ (d == DESKTOP_ALL || d == screen_desktop))
+ {
+ stacking_raise(CLIENT_AS_WINDOW(c));
+ ob_debug("raising window %s", c->title);
+ }
+ }
+ }
+ g_list_free(stacking_copy);
+
+ /* fallback focus like we're changing desktops */
+ if (screen_desktop < screen_num_desktops - 1) {
+ screen_fallback_focus();
+ ob_debug("fake desktop change");
+ }
+
+ screen_set_num_desktops(screen_num_desktops-1);
+
+ event_end_ignore_all_enters(ignore_start);
+}
+
+static void get_row_col(guint d, guint *r, guint *c)
+{
+ switch (screen_desktop_layout.orientation) {
+ case OB_ORIENTATION_HORZ:
+ switch (screen_desktop_layout.start_corner) {
+ case OB_CORNER_TOPLEFT:
+ *r = d / screen_desktop_layout.columns;
+ *c = d % screen_desktop_layout.columns;
+ break;
+ case OB_CORNER_BOTTOMLEFT:
+ *r = screen_desktop_layout.rows - 1 -
+ d / screen_desktop_layout.columns;
+ *c = d % screen_desktop_layout.columns;
+ break;
+ case OB_CORNER_TOPRIGHT:
+ *r = d / screen_desktop_layout.columns;
+ *c = screen_desktop_layout.columns - 1 -
+ d % screen_desktop_layout.columns;
+ break;
+ case OB_CORNER_BOTTOMRIGHT:
+ *r = screen_desktop_layout.rows - 1 -
+ d / screen_desktop_layout.columns;
+ *c = screen_desktop_layout.columns - 1 -
+ d % screen_desktop_layout.columns;
+ break;
+ }
+ break;
+ case OB_ORIENTATION_VERT:
+ switch (screen_desktop_layout.start_corner) {
+ case OB_CORNER_TOPLEFT:
+ *r = d % screen_desktop_layout.rows;
+ *c = d / screen_desktop_layout.rows;
+ break;
+ case OB_CORNER_BOTTOMLEFT:
+ *r = screen_desktop_layout.rows - 1 -
+ d % screen_desktop_layout.rows;
+ *c = d / screen_desktop_layout.rows;
+ break;
+ case OB_CORNER_TOPRIGHT:
+ *r = d % screen_desktop_layout.rows;
+ *c = screen_desktop_layout.columns - 1 -
+ d / screen_desktop_layout.rows;
+ break;
+ case OB_CORNER_BOTTOMRIGHT:
+ *r = screen_desktop_layout.rows - 1 -
+ d % screen_desktop_layout.rows;
+ *c = screen_desktop_layout.columns - 1 -
+ d / screen_desktop_layout.rows;
+ break;
+ }
+ break;
+ }
+}
+
+static guint translate_row_col(guint r, guint c)
+{
+ switch (screen_desktop_layout.orientation) {
+ case OB_ORIENTATION_HORZ:
+ switch (screen_desktop_layout.start_corner) {
+ case OB_CORNER_TOPLEFT:
+ return r % screen_desktop_layout.rows *
+ screen_desktop_layout.columns +
+ c % screen_desktop_layout.columns;
+ case OB_CORNER_BOTTOMLEFT:
+ return (screen_desktop_layout.rows - 1 -
+ r % screen_desktop_layout.rows) *
+ screen_desktop_layout.columns +
+ c % screen_desktop_layout.columns;
+ case OB_CORNER_TOPRIGHT:
+ return r % screen_desktop_layout.rows *
+ screen_desktop_layout.columns +
+ (screen_desktop_layout.columns - 1 -
+ c % screen_desktop_layout.columns);
+ case OB_CORNER_BOTTOMRIGHT:
+ return (screen_desktop_layout.rows - 1 -
+ r % screen_desktop_layout.rows) *
+ screen_desktop_layout.columns +
+ (screen_desktop_layout.columns - 1 -
+ c % screen_desktop_layout.columns);
+ }
+ case OB_ORIENTATION_VERT:
+ switch (screen_desktop_layout.start_corner) {
+ case OB_CORNER_TOPLEFT:
+ return c % screen_desktop_layout.columns *
+ screen_desktop_layout.rows +
+ r % screen_desktop_layout.rows;
+ case OB_CORNER_BOTTOMLEFT:
+ return c % screen_desktop_layout.columns *
+ screen_desktop_layout.rows +
+ (screen_desktop_layout.rows - 1 -
+ r % screen_desktop_layout.rows);
+ case OB_CORNER_TOPRIGHT:
+ return (screen_desktop_layout.columns - 1 -
+ c % screen_desktop_layout.columns) *
+ screen_desktop_layout.rows +
+ r % screen_desktop_layout.rows;
+ case OB_CORNER_BOTTOMRIGHT:
+ return (screen_desktop_layout.columns - 1 -
+ c % screen_desktop_layout.columns) *
+ screen_desktop_layout.rows +
+ (screen_desktop_layout.rows - 1 -
+ r % screen_desktop_layout.rows);
+ }
+ }
+ g_assert_not_reached();
+ return 0;
+}
+
+static gboolean hide_desktop_popup_func(gpointer data)
+{
+ pager_popup_hide(desktop_popup);
+ desktop_popup_timer = 0;
+ return FALSE; /* don't repeat */
+}
+
+void screen_show_desktop_popup(guint d, gboolean perm)
+{
+ const Rect *a;
+
+ /* 0 means don't show the popup */
+ if (!config_desktop_popup_time) return;
+
+ a = screen_physical_area_primary(FALSE);
+ pager_popup_position(desktop_popup, CenterGravity,
+ a->x + a->width / 2, a->y + a->height / 2);
+ pager_popup_icon_size_multiplier(desktop_popup,
+ (screen_desktop_layout.columns /
+ screen_desktop_layout.rows) / 2,
+ (screen_desktop_layout.rows/
+ screen_desktop_layout.columns) / 2);
+ pager_popup_max_width(desktop_popup,
+ MAX(a->width/3, POPUP_WIDTH));
+ pager_popup_show(desktop_popup, screen_desktop_names[d], d);
+
+ if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
+ desktop_popup_timer = 0;
+ if (!perm && !desktop_popup_perm)
+ /* only hide if its not already being show permanently */
+ desktop_popup_timer = g_timeout_add(config_desktop_popup_time,
+ hide_desktop_popup_func,
+ desktop_popup);
+ if (perm)
+ desktop_popup_perm = TRUE;
+}
+
+void screen_hide_desktop_popup(void)
+{
+ if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
+ desktop_popup_timer = 0;
+ pager_popup_hide(desktop_popup);
+ desktop_popup_perm = FALSE;
+}
+
+guint screen_find_desktop(guint from, ObDirection dir,
+ gboolean wrap, gboolean linear)
+{
+ guint r, c;
+ guint d;
+
+ d = from;
+ get_row_col(d, &r, &c);
+ if (linear) {
+ switch (dir) {
+ case OB_DIRECTION_EAST:
+ if (d < screen_num_desktops - 1)
+ ++d;
+ else if (wrap)
+ d = 0;
+ else
+ return from;
+ break;
+ case OB_DIRECTION_WEST:
+ if (d > 0)
+ --d;
+ else if (wrap)
+ d = screen_num_desktops - 1;
+ else
+ return from;
+ break;
+ default:
+ g_assert_not_reached();
+ return from;
+ }
+ } else {
+ switch (dir) {
+ case OB_DIRECTION_EAST:
+ ++c;
+ if (c >= screen_desktop_layout.columns) {
+ if (wrap)
+ c = 0;
+ else
+ return from;
+ }
+ d = translate_row_col(r, c);
+ if (d >= screen_num_desktops) {
+ if (wrap)
+ ++c;
+ else
+ return from;
+ }
+ break;
+ case OB_DIRECTION_WEST:
+ --c;
+ if (c >= screen_desktop_layout.columns) {
+ if (wrap)
+ c = screen_desktop_layout.columns - 1;
+ else
+ return from;
+ }
+ d = translate_row_col(r, c);
+ if (d >= screen_num_desktops) {
+ if (wrap)
+ --c;
+ else
+ return from;
+ }
+ break;
+ case OB_DIRECTION_SOUTH:
+ ++r;
+ if (r >= screen_desktop_layout.rows) {
+ if (wrap)
+ r = 0;
+ else
+ return from;
+ }
+ d = translate_row_col(r, c);
+ if (d >= screen_num_desktops) {
+ if (wrap)
+ ++r;
+ else
+ return from;
+ }
+ break;
+ case OB_DIRECTION_NORTH:
+ --r;
+ if (r >= screen_desktop_layout.rows) {
+ if (wrap)
+ r = screen_desktop_layout.rows - 1;
+ else
+ return from;
+ }
+ d = translate_row_col(r, c);
+ if (d >= screen_num_desktops) {
+ if (wrap)
+ --r;
+ else
+ return from;
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ return from;
+ }
+
+ d = translate_row_col(r, c);
+ }
+ return d;
+}
+
+static gboolean screen_validate_layout(ObDesktopLayout *l)
+{
+ if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
+ return FALSE;
+
+ /* fill in a zero rows/columns */
+ if (l->columns == 0) {
+ l->columns = screen_num_desktops / l->rows;
+ if (l->rows * l->columns < screen_num_desktops)
+ l->columns++;
+ if (l->rows * l->columns >= screen_num_desktops + l->columns)
+ l->rows--;
+ } else if (l->rows == 0) {
+ l->rows = screen_num_desktops / l->columns;
+ if (l->columns * l->rows < screen_num_desktops)
+ l->rows++;
+ if (l->columns * l->rows >= screen_num_desktops + l->rows)
+ l->columns--;
+ }
+
+ /* bounds checking */
+ if (l->orientation == OB_ORIENTATION_HORZ) {
+ l->columns = MIN(screen_num_desktops, l->columns);
+ l->rows = MIN(l->rows,
+ (screen_num_desktops + l->columns - 1) / l->columns);
+ l->columns = screen_num_desktops / l->rows +
+ !!(screen_num_desktops % l->rows);
+ } else {
+ l->rows = MIN(screen_num_desktops, l->rows);
+ l->columns = MIN(l->columns,
+ (screen_num_desktops + l->rows - 1) / l->rows);
+ l->rows = screen_num_desktops / l->columns +
+ !!(screen_num_desktops % l->columns);
+ }
+ return TRUE;
+}
+
+void screen_update_layout(void)
+
+{
+ ObDesktopLayout l;
+ guint32 *data;
+ guint num;
+
+ screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
+ screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
+ screen_desktop_layout.rows = 1;
+ screen_desktop_layout.columns = screen_num_desktops;
+
+ if (OBT_PROP_GETA32(obt_root(ob_screen),
+ NET_DESKTOP_LAYOUT, CARDINAL, &data, &num)) {
+ if (num == 3 || num == 4) {
+
+ if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_VERT))
+ l.orientation = OB_ORIENTATION_VERT;
+ else if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_HORZ))
+ l.orientation = OB_ORIENTATION_HORZ;
+ else
+ return;
+
+ if (num < 4)
+ l.start_corner = OB_CORNER_TOPLEFT;
+ else {
+ if (data[3] == OBT_PROP_ATOM(NET_WM_TOPLEFT))
+ l.start_corner = OB_CORNER_TOPLEFT;
+ else if (data[3] == OBT_PROP_ATOM(NET_WM_TOPRIGHT))
+ l.start_corner = OB_CORNER_TOPRIGHT;
+ else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMRIGHT))
+ l.start_corner = OB_CORNER_BOTTOMRIGHT;
+ else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMLEFT))
+ l.start_corner = OB_CORNER_BOTTOMLEFT;
+ else
+ return;
+ }
+
+ l.columns = data[1];
+ l.rows = data[2];
+
+ if (screen_validate_layout(&l))
+ screen_desktop_layout = l;
+
+ g_free(data);
+ }
+ }
+}
+
+void screen_update_desktop_names(void)
+{
+ guint i;
+
+ /* empty the array */
+ g_strfreev(screen_desktop_names);
+ screen_desktop_names = NULL;
+
+ if (OBT_PROP_GETSS(obt_root(ob_screen),
+ NET_DESKTOP_NAMES, &screen_desktop_names))
+ for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
+ else
+ i = 0;
+ if (i < screen_num_desktops) {
+ GSList *it;
+
+ screen_desktop_names = g_renew(gchar*, screen_desktop_names,
+ screen_num_desktops + 1);
+ screen_desktop_names[screen_num_desktops] = NULL;
+
+ it = g_slist_nth(config_desktops_names, i);
+
+ for (; i < screen_num_desktops; ++i) {
+ if (it && ((char*)it->data)[0]) /* not empty */
+ /* use the names from the config file when possible */
+ screen_desktop_names[i] = g_strdup(it->data);
+ else
+ /* make up a nice name if it's not though */
+ screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
+ i + 1);
+ if (it) it = g_slist_next(it);
+ }
+
+ /* if we changed any names, then set the root property so we can
+ all agree on the names */
+ OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES,
+ (const gchar*const*)screen_desktop_names);
+ }
+
+ /* resize the pager for these names */
+ pager_popup_text_width_to_strings(desktop_popup,
+ screen_desktop_names,
+ screen_num_desktops);
+}
+
+void screen_show_desktop(gboolean show, ObClient *show_only)
+{
+ GList *it;
+
+ if (show == screen_showing_desktop) return; /* no change */
+
+ screen_showing_desktop = show;
+
+ if (show) {
+ /* hide windows bottom to top */
+ for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
+ if (WINDOW_IS_CLIENT(it->data)) {
+ ObClient *client = it->data;
+ client_showhide(client);
+ }
+ }
+ }
+ else {
+ /* restore windows top to bottom */
+ for (it = stacking_list; it; it = g_list_next(it)) {
+ if (WINDOW_IS_CLIENT(it->data)) {
+ ObClient *client = it->data;
+ if (client_should_show(client)) {
+ if (!show_only || client == show_only)
+ client_show(client);
+ else
+ client_iconify(client, TRUE, FALSE, TRUE);
+ }
+ }
+ }
+ }
+
+ if (show) {
+ /* focus the desktop */
+ for (it = focus_order; it; it = g_list_next(it)) {
+ ObClient *c = it->data;
+ if (c->type == OB_CLIENT_TYPE_DESKTOP &&
+ (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
+ client_focus(it->data))
+ break;
+ }
+ }
+ else if (!show_only) {
+ ObClient *c;
+
+ if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) {
+ /* only do the flicker reducing stuff ahead of time if we are going
+ to call xsetinputfocus on the window ourselves. otherwise there
+ is no guarantee the window will actually take focus.. */
+ if (c->can_focus) {
+ /* reduce flicker by hiliting now rather than waiting for the
+ server FocusIn event */
+ frame_adjust_focus(c->frame, TRUE);
+ }
+ }
+ }
+
+ show = !!show; /* make it boolean */
+ OBT_PROP_SET32(obt_root(ob_screen), NET_SHOWING_DESKTOP, CARDINAL, show);
+}
+
+void screen_install_colormap(ObClient *client, gboolean install)
+{
+ if (client == NULL || client->colormap == None) {
+ if (install)
+ XInstallColormap(obt_display, RrColormap(ob_rr_inst));
+ else
+ XUninstallColormap(obt_display, RrColormap(ob_rr_inst));
+ } else {
+ obt_display_ignore_errors(TRUE);
+ if (install)
+ XInstallColormap(obt_display, client->colormap);
+ else
+ XUninstallColormap(obt_display, client->colormap);
+ obt_display_ignore_errors(FALSE);
+ }
+}
+
+typedef struct {
+ guint desktop;
+ StrutPartial *strut;
+} ObScreenStrut;
+
+#define RESET_STRUT_LIST(sl) \
+ while (sl) { \
+ g_slice_free(ObScreenStrut, (sl)->data); \
+ sl = g_slist_delete_link(sl, sl); \
+ }
+
+#define ADD_STRUT_TO_LIST(sl, d, s) \
+{ \
+ ObScreenStrut *ss = g_slice_new(ObScreenStrut); \
+ ss->desktop = d; \
+ ss->strut = s; \
+ sl = g_slist_prepend(sl, ss); \
+}
+
+#define VALIDATE_STRUTS(sl, side, max) \
+{ \
+ GSList *it; \
+ for (it = sl; it; it = g_slist_next(it)) { \
+ ObScreenStrut *ss = it->data; \
+ ss->strut->side = MIN(max, ss->strut->side); \
+ } \
+}
+
+static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
+{
+ guint i;
+ gint n, l, r, t, b;
+#ifdef XINERAMA
+ XineramaScreenInfo *info;
+#endif
+
+ if (ob_debug_xinerama) {
+ gint w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
+ gint h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
+ *nxin = 2;
+ *xin_areas = g_new(Rect, *nxin + 1);
+ RECT_SET((*xin_areas)[0], 0, 0, w/2, h);
+ RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h);
+ }
+#ifdef XINERAMA
+ else if (obt_display_extension_xinerama &&
+ (info = XineramaQueryScreens(obt_display, &n))) {
+ *nxin = n;
+ *xin_areas = g_new(Rect, *nxin + 1);
+ for (i = 0; i < *nxin; ++i)
+ RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org,
+ info[i].width, info[i].height);
+ XFree(info);
+ }
+#endif
+ else {
+ *nxin = 1;
+ *xin_areas = g_new(Rect, *nxin + 1);
+ RECT_SET((*xin_areas)[0], 0, 0,
+ WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
+ HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
+ }
+
+ /* returns one extra with the total area in it */
+ l = (*xin_areas)[0].x;
+ t = (*xin_areas)[0].y;
+ r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1;
+ b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1;
+ for (i = 1; i < *nxin; ++i) {
+ l = MIN(l, (*xin_areas)[i].x);
+ t = MIN(l, (*xin_areas)[i].y);
+ r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);
+ 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);
+}
+
+void screen_update_areas(void)
+{
+ guint i;
+ gulong *dims;
+ GList *it, *onscreen;
+
+ /* collect the clients that are on screen */
+ onscreen = NULL;
+ for (it = client_list; it; it = g_list_next(it)) {
+ if (client_monitor(it->data) != screen_num_monitors)
+ onscreen = g_list_prepend(onscreen, it->data);
+ }
+
+ g_free(monitor_area);
+ get_xinerama_screens(&monitor_area, &screen_num_monitors);
+
+ /* set up the user-specified margins */
+ config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]);
+ config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
+ config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]);
+ config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
+ config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]);
+ config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
+ config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
+ config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
+
+ RESET_STRUT_LIST(struts_left);
+ RESET_STRUT_LIST(struts_top);
+ RESET_STRUT_LIST(struts_right);
+ RESET_STRUT_LIST(struts_bottom);
+
+ /* collect the struts */
+ for (it = client_list; it; it = g_list_next(it)) {
+ ObClient *c = it->data;
+ if (c->strut.left)
+ ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut);
+ if (c->strut.top)
+ ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut);
+ if (c->strut.right)
+ ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut);
+ if (c->strut.bottom)
+ ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut);
+ }
+ if (dock_strut.left)
+ ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut);
+ if (dock_strut.top)
+ ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut);
+ if (dock_strut.right)
+ ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut);
+ if (dock_strut.bottom)
+ ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut);
+
+ if (config_margins.left)
+ ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins);
+ if (config_margins.top)
+ ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins);
+ if (config_margins.right)
+ ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins);
+ if (config_margins.bottom)
+ ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins);
+
+ VALIDATE_STRUTS(struts_left, left,
+ monitor_area[screen_num_monitors].width / 2);
+ VALIDATE_STRUTS(struts_right, right,
+ monitor_area[screen_num_monitors].width / 2);
+ VALIDATE_STRUTS(struts_top, top,
+ monitor_area[screen_num_monitors].height / 2);
+ VALIDATE_STRUTS(struts_bottom, bottom,
+ monitor_area[screen_num_monitors].height / 2);
+
+ dims = g_new(gulong, 4 * screen_num_desktops);
+ for (i = 0; i < screen_num_desktops; ++i) {
+ Rect *area = screen_area(i, SCREEN_AREA_ALL_MONITORS, NULL);
+ dims[i*4+0] = area->x;
+ dims[i*4+1] = area->y;
+ dims[i*4+2] = area->width;
+ dims[i*4+3] = area->height;
+ g_slice_free(Rect, area);
+ }
+
+ /* set the legacy workarea hint to the union of all the monitors */
+ OBT_PROP_SETA32(obt_root(ob_screen), NET_WORKAREA, CARDINAL,
+ dims, 4 * screen_num_desktops);
+
+ /* the area has changed, adjust all the windows if they need it */
+ for (it = onscreen; it; it = g_list_next(it))
+ client_reconfigure(it->data, FALSE);
+
+ g_free(dims);
+}
+
+#if 0
+Rect* screen_area_all_monitors(guint desktop)
+{
+ guint i;
+ Rect *a;
+
+ a = screen_area_monitor(desktop, 0);
+
+ /* combine all the monitors together */
+ for (i = 1; i < screen_num_monitors; ++i) {
+ Rect *m = screen_area_monitor(desktop, i);
+ gint l, r, t, b;
+
+ l = MIN(RECT_LEFT(*a), RECT_LEFT(*m));
+ t = MIN(RECT_TOP(*a), RECT_TOP(*m));
+ r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m));
+ b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m));
+
+ RECT_SET(*a, l, t, r - l + 1, b - t + 1);
+
+ g_free(m);
+ }
+
+ return a;
+}
+#endif
+
+#define STRUT_LEFT_IN_SEARCH(s, search) \
+ (RANGES_INTERSECT(search->y, search->height, \
+ s->left_start, s->left_end - s->left_start + 1))
+#define STRUT_RIGHT_IN_SEARCH(s, search) \
+ (RANGES_INTERSECT(search->y, search->height, \
+ s->right_start, s->right_end - s->right_start + 1))
+#define STRUT_TOP_IN_SEARCH(s, search) \
+ (RANGES_INTERSECT(search->x, search->width, \
+ s->top_start, s->top_end - s->top_start + 1))
+#define STRUT_BOTTOM_IN_SEARCH(s, search) \
+ (RANGES_INTERSECT(search->x, search->width, \
+ s->bottom_start, s->bottom_end - s->bottom_start + 1))
+
+#define STRUT_LEFT_IGNORE(s, us, search) \
+ (head == SCREEN_AREA_ALL_MONITORS && us && \
+ RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search))
+#define STRUT_RIGHT_IGNORE(s, us, search) \
+ (head == SCREEN_AREA_ALL_MONITORS && us && \
+ RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search))
+#define STRUT_TOP_IGNORE(s, us, search) \
+ (head == SCREEN_AREA_ALL_MONITORS && us && \
+ RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search))
+#define STRUT_BOTTOM_IGNORE(s, us, search) \
+ (head == SCREEN_AREA_ALL_MONITORS && us && \
+ RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search))
+
+Rect* screen_area(guint desktop, guint head, Rect *search)
+{
+ Rect *a;
+ GSList *it;
+ gint l, r, t, b;
+ guint i, d;
+ gboolean us = search != NULL; /* user provided search */
+
+ g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL);
+ g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR ||
+ head == SCREEN_AREA_ALL_MONITORS);
+ g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL));
+
+ /* find any struts for this monitor
+ which will be affecting the search area.
+ */
+
+ /* search everything if search is null */
+ if (!search) {
+ if (head < screen_num_monitors) search = &monitor_area[head];
+ else search = &monitor_area[screen_num_monitors];
+ }
+ if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search);
+
+ /* al is "all left" meaning the furthest left you can get, l is our
+ "working left" meaning our current strut edge which we're calculating
+ */
+
+ /* only include monitors which the search area lines up with */
+ if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) {
+ l = RECT_RIGHT(monitor_area[screen_num_monitors]);
+ t = RECT_BOTTOM(monitor_area[screen_num_monitors]);
+ r = RECT_LEFT(monitor_area[screen_num_monitors]);
+ b = RECT_TOP(monitor_area[screen_num_monitors]);
+ for (i = 0; i < screen_num_monitors; ++i) {
+ /* add the monitor if applicable */
+ if (RANGES_INTERSECT(search->x, search->width,
+ monitor_area[i].x, monitor_area[i].width))
+ {
+ t = MIN(t, RECT_TOP(monitor_area[i]));
+ b = MAX(b, RECT_BOTTOM(monitor_area[i]));
+ }
+ if (RANGES_INTERSECT(search->y, search->height,
+ monitor_area[i].y, monitor_area[i].height))
+ {
+ l = MIN(l, RECT_LEFT(monitor_area[i]));
+ r = MAX(r, RECT_RIGHT(monitor_area[i]));
+ }
+ }
+ } else {
+ l = RECT_LEFT(monitor_area[screen_num_monitors]);
+ t = RECT_TOP(monitor_area[screen_num_monitors]);
+ r = RECT_RIGHT(monitor_area[screen_num_monitors]);
+ b = RECT_BOTTOM(monitor_area[screen_num_monitors]);
+ }
+
+ for (d = 0; d < screen_num_desktops; ++d) {
+ if (d != desktop && desktop != DESKTOP_ALL) continue;
+
+ for (i = 0; i < screen_num_monitors; ++i) {
+ if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue;
+
+ for (it = struts_left; it; it = g_slist_next(it)) {
+ ObScreenStrut *s = it->data;
+ if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
+ STRUT_LEFT_IN_SEARCH(s->strut, search) &&
+ !STRUT_LEFT_IGNORE(s->strut, us, search))
+ l = MAX(l, RECT_LEFT(monitor_area[screen_num_monitors])
+ + s->strut->left);
+ }
+ for (it = struts_top; it; it = g_slist_next(it)) {
+ ObScreenStrut *s = it->data;
+ if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
+ STRUT_TOP_IN_SEARCH(s->strut, search) &&
+ !STRUT_TOP_IGNORE(s->strut, us, search))
+ t = MAX(t, RECT_TOP(monitor_area[screen_num_monitors])
+ + s->strut->top);
+ }
+ for (it = struts_right; it; it = g_slist_next(it)) {
+ ObScreenStrut *s = it->data;
+ if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
+ STRUT_RIGHT_IN_SEARCH(s->strut, search) &&
+ !STRUT_RIGHT_IGNORE(s->strut, us, search))
+ r = MIN(r, RECT_RIGHT(monitor_area[screen_num_monitors])
+ - s->strut->right);
+ }
+ for (it = struts_bottom; it; it = g_slist_next(it)) {
+ ObScreenStrut *s = it->data;
+ if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
+ STRUT_BOTTOM_IN_SEARCH(s->strut, search) &&
+ !STRUT_BOTTOM_IGNORE(s->strut, us, search))
+ b = MIN(b, RECT_BOTTOM(monitor_area[screen_num_monitors])
+ - s->strut->bottom);
+ }
+
+ /* limit to this monitor */
+ if (head == i) {
+ l = MAX(l, RECT_LEFT(monitor_area[i]));
+ t = MAX(t, RECT_TOP(monitor_area[i]));
+ r = MIN(r, RECT_RIGHT(monitor_area[i]));
+ b = MIN(b, RECT_BOTTOM(monitor_area[i]));
+ }
+ }
+ }
+
+ a = g_slice_new(Rect);
+ a->x = l;
+ a->y = t;
+ a->width = r - l + 1;
+ a->height = b - t + 1;
+ return a;
+}
+
+guint screen_find_monitor(const Rect *search)
+{
+ guint i;
+ guint most = screen_num_monitors;
+ guint mostv = 0;
+
+ 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;
+
+ RECT_SET_INTERSECTION(r, *area, *search);
+ v = r.width * r.height;
+
+ if (v > mostv) {
+ mostv = v;
+ most = i;
+ }
+ }
+ }
+ return most < screen_num_monitors ? most : screen_monitor_primary(FALSE);
+}
+
+const Rect* screen_physical_area_all_monitors(void)
+{
+ return screen_physical_area_monitor(screen_num_monitors);
+}
+
+const Rect* screen_physical_area_monitor(guint head)
+{
+ g_assert(head <= screen_num_monitors);
+
+ return &monitor_area[head];
+}
+
+gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
+{
+ g_assert(head <= screen_num_monitors);
+ g_assert(search);
+ return RECT_INTERSECTS_RECT(monitor_area[head], *search);
+}
+
+guint screen_monitor_active(void)
+{
+ if (moveresize_client)
+ return client_monitor(moveresize_client);
+ else if (focus_client)
+ return client_monitor(focus_client);
+ else
+ return screen_monitor_pointer();
+}
+
+const Rect* screen_physical_area_active(void)
+{
+ return screen_physical_area_monitor(screen_monitor_active());
+}
+
+guint screen_monitor_primary(gboolean fixed)
+{
+ if (config_primary_monitor_index > 0) {
+ if (config_primary_monitor_index-1 < screen_num_monitors)
+ return config_primary_monitor_index - 1;
+ else
+ return 0;
+ }
+ else if (fixed)
+ return 0;
+ else if (config_primary_monitor == OB_PLACE_MONITOR_ACTIVE)
+ return screen_monitor_active();
+ else /* config_primary_monitor == OB_PLACE_MONITOR_MOUSE */
+ return screen_monitor_pointer();
+}
+
+const Rect* screen_physical_area_primary(gboolean fixed)
+{
+ return screen_physical_area_monitor(screen_monitor_primary(fixed));
+}
+
+void screen_set_root_cursor(void)
+{
+ if (sn_app_starting())
+ XDefineCursor(obt_display, obt_root(ob_screen),
+ ob_cursor(OB_CURSOR_BUSYPOINTER));
+ else
+ XDefineCursor(obt_display, obt_root(ob_screen),
+ ob_cursor(OB_CURSOR_POINTER));
+}
+
+guint screen_find_monitor_point(guint x, guint y)
+{
+ Rect mon;
+ RECT_SET(mon, x, y, 1, 1);
+ return screen_find_monitor(&mon);
+}
+
+guint screen_monitor_pointer()
+{
+ gint x, y;
+ if (!screen_pointer_pos(&x, &y))
+ x = y = 0;
+ return screen_find_monitor_point(x, y);
+}
+
+gboolean screen_pointer_pos(gint *x, gint *y)
+{
+ Window w;
+ gint i;
+ guint u;
+ gboolean ret;
+
+ ret = !!XQueryPointer(obt_display, obt_root(ob_screen),
+ &w, &w, x, y, &i, &i, &u);
+ if (!ret) {
+ for (i = 0; i < ScreenCount(obt_display); ++i)
+ if (i != ob_screen)
+ if (XQueryPointer(obt_display, obt_root(i),
+ &w, &w, x, y, &i, &i, &u))
+ break;
+ }
+ return ret;
+}
+
+gboolean screen_compare_desktops(guint a, guint b)
+{
+ if (a == DESKTOP_ALL)
+ a = screen_desktop;
+ if (b == DESKTOP_ALL)
+ b = screen_desktop;
+ return a == b;
+}
diff --git a/openbox/screen.h b/openbox/screen.h
new file mode 100644
index 0000000..a6a3995
--- /dev/null
+++ b/openbox/screen.h
@@ -0,0 +1,175 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ screen.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __screen_h
+#define __screen_h
+
+#include "misc.h"
+#include "geom.h"
+
+struct _ObClient;
+
+#define DESKTOP_ALL (0xffffffff)
+
+/*! The number of available desktops */
+extern guint screen_num_desktops;
+/*! The number of virtual "xinerama" screens/heads */
+extern guint screen_num_monitors;
+/*! The current desktop */
+extern guint screen_desktop;
+/*! The desktop which was last visible */
+extern guint screen_last_desktop;
+/*! Are we in showing-desktop mode? */
+extern gboolean screen_showing_desktop;
+/*! The support window also used for focus and stacking */
+extern Window screen_support_win;
+/*! The last time at which the user changed desktops */
+extern Time screen_desktop_user_time;
+
+typedef struct ObDesktopLayout {
+ ObOrientation orientation;
+ ObCorner start_corner;
+ guint rows;
+ guint columns;
+} ObDesktopLayout;
+extern ObDesktopLayout screen_desktop_layout;
+
+/*! An array of gchar*'s which are desktop names in UTF-8 format */
+extern gchar **screen_desktop_names;
+
+/*! Take over the screen, set the basic hints on it claming it as ours */
+gboolean screen_annex(void);
+
+/*! Once the screen is ours, set up its initial state */
+void screen_startup(gboolean reconfig);
+/*! Free resources */
+void screen_shutdown(gboolean reconfig);
+
+/*! Figure out the new size of the screen and adjust stuff for it */
+void screen_resize(void);
+
+/*! Change the number of available desktops */
+void screen_set_num_desktops(guint num);
+/*! Change the current desktop */
+void screen_set_desktop(guint num, gboolean dofocus);
+/*! Add a new desktop either at the end or inserted at the current desktop */
+void screen_add_desktop(gboolean current);
+/*! Remove a desktop, either at the end or the current desktop */
+void screen_remove_desktop(gboolean current);
+
+guint screen_find_desktop(guint from, ObDirection dir,
+ gboolean wrap, gboolean linear);
+
+/*! Show the desktop popup/notification
+ @permanent If TRUE, the popup will stay on the screen until you call
+ screen_hide_desktop_popup(). Otherwise it will hide after a
+ delay.
+ */
+void screen_show_desktop_popup(guint d, gboolean permanent);
+/*! Hide it */
+void screen_hide_desktop_popup(void);
+
+/*! Shows and focuses the desktop and hides all the client windows, or
+ returns to the normal state, showing client windows.
+ @param If show_only is non-NULL, then only that client is shown (assuming
+ show is FALSE (restoring from show-desktop mode), and the rest are
+ iconified.
+*/
+void screen_show_desktop(gboolean show, struct _ObClient *show_only);
+
+/*! Updates the desktop layout from the root property if available */
+void screen_update_layout(void);
+
+/*! Get desktop names from the root window property */
+void screen_update_desktop_names(void);
+
+/*! Installs or uninstalls a colormap for a client. If client is NULL, then
+ it handles the root colormap. */
+void screen_install_colormap(struct _ObClient *client, gboolean install);
+
+void screen_update_areas(void);
+
+const Rect* screen_physical_area_all_monitors(void);
+
+/*! Returns a Rect which is owned by the screen code and should not be freed */
+const Rect* screen_physical_area_monitor(guint head);
+
+/*! Returns the monitor which contains the active window, or the one
+ containing the pointer otherwise. */
+guint screen_monitor_active(void);
+
+/*! Returns a Rect which is owned by the screen code and should not be freed */
+const Rect* screen_physical_area_active(void);
+
+/*! Returns the primary monitor, as specified by the config.
+ @fixed If TRUE, then this will always return a fixed monitor, otherwise
+ it may change based on where focus is, or other heuristics.
+ */
+guint screen_monitor_primary(gboolean fixed);
+
+/*! Returns physical area for the primary monitor, as specified by the config.
+ @fixed If TRUE, then this will always use a fixed monitor as primary,
+ otherwise it may change based on where focus is, or other heuristics.
+ See screen_monitor_primary().
+ @return A Rect which is owned by the screen code and should not be freed
+*/
+const Rect* screen_physical_area_primary(gboolean fixed);
+
+/* doesn't include struts which the search area is already outside of when
+ 'search' is not NULL */
+#define SCREEN_AREA_ALL_MONITORS ((unsigned)-1)
+#define SCREEN_AREA_ONE_MONITOR ((unsigned)-2)
+
+/*! @param head is the number of the head or one of SCREEN_AREA_ALL_MONITORS,
+ SCREEN_AREA_ONE_MONITOR
+ @param search NULL or the whole monitor(s)
+ @return A Rect allocated with g_slice_new()
+ */
+Rect* screen_area(guint desktop, guint head, Rect *search);
+
+gboolean screen_physical_area_monitor_contains(guint head, Rect *search);
+
+/*! Determines which physical monitor a rectangle is on by calculating the
+ area of the part of the rectable on each monitor. The number of the
+ monitor containing the greatest area of the rectangle is returned.
+*/
+guint screen_find_monitor(const Rect *search);
+
+/*! Finds the monitor which contains the point @x, @y */
+guint screen_find_monitor_point(guint x, guint y);
+
+/*! Sets the root cursor. This function decides which cursor to use, but you
+ gotta call it to let it know it should change. */
+void screen_set_root_cursor(void);
+
+/*! Gives back the pointer's position in x and y. Returns TRUE if the pointer
+ is on this screen and FALSE if it is on another screen. */
+gboolean screen_pointer_pos(gint *x, gint *y);
+
+/*! Returns the monitor which contains the pointer device */
+guint screen_monitor_pointer(void);
+
+/*! Compare the desktop for two windows to see if they are considered on the
+ same desktop.
+ Windows that are on "all desktops" are treated like they are only on the
+ current desktop, so they are only in one place at a time.
+ @return TRUE if they are on the same desktop, FALSE otherwise.
+*/
+gboolean screen_compare_desktops(guint a, guint b);
+
+#endif
diff --git a/openbox/session.c b/openbox/session.c
new file mode 100644
index 0000000..d6c6f76
--- /dev/null
+++ b/openbox/session.c
@@ -0,0 +1,866 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ session.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+/* This session code is largely inspired by metacity code. */
+
+#include "session.h"
+
+struct _ObClient;
+
+GList *session_saved_state = NULL;
+gint session_desktop = -1;
+gint session_num_desktops = 0;
+gboolean session_desktop_layout_present = FALSE;
+ObDesktopLayout session_desktop_layout;
+GSList *session_desktop_names = NULL;
+
+#ifndef USE_SM
+void session_startup(gint argc, gchar **argv) {}
+void session_shutdown(gboolean permanent) {}
+GList* session_state_find(struct _ObClient *c) { return NULL; }
+void session_request_logout(gboolean silent) {}
+gboolean session_connected(void) { return FALSE; }
+#else
+
+#include "debug.h"
+#include "openbox.h"
+#include "client.h"
+#include "focus.h"
+#include "gettext.h"
+#include "obt/xml.h"
+#include "obt/paths.h"
+
+#include <time.h>
+#include <errno.h>
+#include <stdio.h>
+
+#ifdef HAVE_UNISTD_H
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+
+#include <X11/SM/SMlib.h>
+
+#define SM_ERR_LEN 1024
+
+static SmcConn sm_conn;
+static gint sm_argc;
+static gchar **sm_argv;
+
+/* Data saved from the first level save yourself */
+typedef struct {
+ ObClient *focus_client;
+ gint desktop;
+} ObSMSaveData;
+
+static gboolean session_connect();
+
+static void session_load_file(const gchar *path);
+static gboolean session_save_to_file(const ObSMSaveData *savedata);
+
+static void session_setup_program();
+static void session_setup_user();
+static void session_setup_restart_style(gboolean restart);
+static void session_setup_pid();
+static void session_setup_priority();
+static void session_setup_clone_command();
+static void session_setup_restart_command();
+
+static void sm_save_yourself(SmcConn conn, SmPointer data, gint save_type,
+ Bool shutdown, gint interact_style, Bool fast);
+static void sm_die(SmcConn conn, SmPointer data);
+static void sm_save_complete(SmcConn conn, SmPointer data);
+static void sm_shutdown_cancelled(SmcConn conn, SmPointer data);
+
+static gboolean session_state_cmp(ObSessionState *s, ObClient *c);
+static void session_state_free(ObSessionState *state);
+
+void session_startup(gint argc, gchar **argv)
+{
+ gchar *dir;
+ ObtPaths *p;
+
+ if (!ob_sm_use) return;
+
+ sm_argc = argc;
+ sm_argv = argv;
+
+ p = obt_paths_new();
+ dir = g_build_filename(obt_paths_cache_home(p),
+ "openbox", "sessions", NULL);
+ obt_paths_unref(p), p = NULL;
+
+ if (!obt_paths_mkdir_path(dir, 0700)) {
+ g_message(_("Unable to make directory \"%s\": %s"),
+ dir, g_strerror(errno));
+ }
+
+ if (ob_sm_save_file != NULL) {
+ if (ob_sm_restore) {
+ ob_debug_type(OB_DEBUG_SM, "Loading from session file %s",
+ ob_sm_save_file);
+ session_load_file(ob_sm_save_file);
+ }
+ } else {
+ gchar *filename;
+
+ /* this algo is from metacity */
+ filename = g_strdup_printf("%u-%u-%u.obs",
+ (guint)time(NULL),
+ (guint)getpid(),
+ g_random_int());
+ ob_sm_save_file = g_build_filename(dir, filename, NULL);
+ g_free(filename);
+ }
+
+ if (session_connect()) {
+ session_setup_program();
+ session_setup_user();
+ session_setup_restart_style(TRUE);
+ session_setup_pid();
+ session_setup_priority();
+ session_setup_clone_command();
+ }
+
+ g_free(dir);
+}
+
+void session_shutdown(gboolean permanent)
+{
+ if (!ob_sm_use) return;
+
+ if (sm_conn) {
+ /* if permanent is true then we will change our session state so that
+ the SM won't run us again */
+ if (permanent)
+ session_setup_restart_style(FALSE);
+
+ SmcCloseConnection(sm_conn, 0, NULL);
+
+ while (session_saved_state) {
+ session_state_free(session_saved_state->data);
+ session_saved_state = g_list_delete_link(session_saved_state,
+ session_saved_state);
+ }
+ }
+}
+
+gboolean session_connected(void)
+{
+ return !!sm_conn;
+}
+
+/*! Connect to the session manager and set up our callback functions */
+static gboolean session_connect(void)
+{
+ SmcCallbacks cb;
+ gchar *oldid;
+ gchar sm_err[SM_ERR_LEN];
+
+ /* set up our callback functions */
+ cb.save_yourself.callback = sm_save_yourself;
+ cb.save_yourself.client_data = NULL;
+ cb.die.callback = sm_die;
+ cb.die.client_data = NULL;
+ cb.save_complete.callback = sm_save_complete;
+ cb.save_complete.client_data = NULL;
+ cb.shutdown_cancelled.callback = sm_shutdown_cancelled;
+ cb.shutdown_cancelled.client_data = NULL;
+
+ /* connect to the server */
+ oldid = ob_sm_id;
+ ob_debug_type(OB_DEBUG_SM, "Connecting to SM with id: %s",
+ oldid ? oldid : "(null)");
+ sm_conn = SmcOpenConnection(NULL, NULL, 1, 0,
+ SmcSaveYourselfProcMask |
+ SmcDieProcMask |
+ SmcSaveCompleteProcMask |
+ SmcShutdownCancelledProcMask,
+ &cb, oldid, &ob_sm_id,
+ SM_ERR_LEN-1, sm_err);
+ g_free(oldid);
+ ob_debug_type(OB_DEBUG_SM, "Connected to SM with id: %s", ob_sm_id);
+ if (sm_conn == NULL)
+ ob_debug("Failed to connect to session manager: %s", sm_err);
+ return sm_conn != NULL;
+}
+
+static void session_setup_program(void)
+{
+ SmPropValue vals = {
+ .value = sm_argv[0],
+ .length = strlen(sm_argv[0]) + 1
+ };
+ SmProp prop = {
+ .name = g_strdup(SmProgram),
+ .type = g_strdup(SmARRAY8),
+ .num_vals = 1,
+ .vals = &vals
+ };
+ SmProp *list = &prop;
+ ob_debug_type(OB_DEBUG_SM, "Setting program: %s", sm_argv[0]);
+ SmcSetProperties(sm_conn, 1, &list);
+ g_free(prop.name);
+ g_free(prop.type);
+}
+
+static void session_setup_user(void)
+{
+ char *user = g_strdup(g_get_user_name());
+
+ SmPropValue vals = {
+ .value = user,
+ .length = strlen(user) + 1
+ };
+ SmProp prop = {
+ .name = g_strdup(SmUserID),
+ .type = g_strdup(SmARRAY8),
+ .num_vals = 1,
+ .vals = &vals
+ };
+ SmProp *list = &prop;
+ ob_debug_type(OB_DEBUG_SM, "Setting user: %s", user);
+ SmcSetProperties(sm_conn, 1, &list);
+ g_free(prop.name);
+ g_free(prop.type);
+ g_free(user);
+}
+
+static void session_setup_restart_style(gboolean restart)
+{
+ gchar restart_hint = restart ? SmRestartImmediately : SmRestartIfRunning;
+
+ SmPropValue vals = {
+ .value = &restart_hint,
+ .length = 1
+ };
+ SmProp prop = {
+ .name = g_strdup(SmRestartStyleHint),
+ .type = g_strdup(SmCARD8),
+ .num_vals = 1,
+ .vals = &vals
+ };
+ SmProp *list = &prop;
+ ob_debug_type(OB_DEBUG_SM, "Setting restart: %d", restart);
+ SmcSetProperties(sm_conn, 1, &list);
+ g_free(prop.name);
+ g_free(prop.type);
+}
+
+static void session_setup_pid(void)
+{
+ gchar *pid = g_strdup_printf("%ld", (glong) getpid());
+
+ SmPropValue vals = {
+ .value = pid,
+ .length = strlen(pid) + 1
+ };
+ SmProp prop = {
+ .name = g_strdup(SmProcessID),
+ .type = g_strdup(SmARRAY8),
+ .num_vals = 1,
+ .vals = &vals
+ };
+ SmProp *list = &prop;
+ ob_debug_type(OB_DEBUG_SM, "Setting pid: %s", pid);
+ SmcSetProperties(sm_conn, 1, &list);
+ g_free(prop.name);
+ g_free(prop.type);
+ g_free(pid);
+}
+
+/*! This is a gnome-session-manager extension */
+static void session_setup_priority(void)
+{
+ gchar priority = 20; /* 20 is a lower prioity to run before other apps */
+
+ SmPropValue vals = {
+ .value = &priority,
+ .length = 1
+ };
+ SmProp prop = {
+ .name = g_strdup("_GSM_Priority"),
+ .type = g_strdup(SmCARD8),
+ .num_vals = 1,
+ .vals = &vals
+ };
+ SmProp *list = &prop;
+ ob_debug_type(OB_DEBUG_SM, "Setting priority: %d", priority);
+ SmcSetProperties(sm_conn, 1, &list);
+ g_free(prop.name);
+ g_free(prop.type);
+}
+
+static void session_setup_clone_command(void)
+{
+ gint i;
+
+ SmPropValue *vals = g_new(SmPropValue, sm_argc);
+ SmProp prop = {
+ .name = g_strdup(SmCloneCommand),
+ .type = g_strdup(SmLISTofARRAY8),
+ .num_vals = sm_argc,
+ .vals = vals
+ };
+ SmProp *list = &prop;
+
+ ob_debug_type(OB_DEBUG_SM, "Setting clone command: (%d)", sm_argc);
+ for (i = 0; i < sm_argc; ++i) {
+ vals[i].value = sm_argv[i];
+ vals[i].length = strlen(sm_argv[i]) + 1;
+ ob_debug_type(OB_DEBUG_SM, " %s", vals[i].value);
+ }
+
+ SmcSetProperties(sm_conn, 1, &list);
+ g_free(prop.name);
+ g_free(prop.type);
+ g_free(vals);
+}
+
+static void session_setup_restart_command(void)
+{
+ gint i;
+
+ SmPropValue *vals = g_new(SmPropValue, sm_argc + 4);
+ SmProp prop = {
+ .name = g_strdup(SmRestartCommand),
+ .type = g_strdup(SmLISTofARRAY8),
+ .num_vals = sm_argc + 4,
+ .vals = vals
+ };
+ SmProp *list = &prop;
+
+ ob_debug_type(OB_DEBUG_SM, "Setting restart command: (%d)", sm_argc+4);
+ for (i = 0; i < sm_argc; ++i) {
+ vals[i].value = sm_argv[i];
+ vals[i].length = strlen(sm_argv[i]) + 1;
+ ob_debug_type(OB_DEBUG_SM, " %s", vals[i].value);
+ }
+
+ vals[i].value = g_strdup("--sm-client-id");
+ vals[i].length = strlen("--sm-client-id") + 1;
+ vals[i+1].value = ob_sm_id;
+ vals[i+1].length = strlen(ob_sm_id) + 1;
+ ob_debug_type(OB_DEBUG_SM, " %s", vals[i].value);
+ ob_debug_type(OB_DEBUG_SM, " %s", vals[i+1].value);
+
+ vals[i+2].value = g_strdup("--sm-save-file");
+ vals[i+2].length = strlen("--sm-save-file") + 1;
+ vals[i+3].value = ob_sm_save_file;
+ vals[i+3].length = strlen(ob_sm_save_file) + 1;
+ ob_debug_type(OB_DEBUG_SM, " %s", vals[i+2].value);
+ ob_debug_type(OB_DEBUG_SM, " %s", vals[i+3].value);
+
+ SmcSetProperties(sm_conn, 1, &list);
+ g_free(prop.name);
+ g_free(prop.type);
+ g_free(vals[i].value);
+ g_free(vals[i+2].value);
+ g_free(vals);
+}
+
+static ObSMSaveData *sm_save_get_data(void)
+{
+ ObSMSaveData *savedata = g_slice_new0(ObSMSaveData);
+ /* save the active desktop and client.
+ we don't bother to preemptively save the other desktop state like
+ number and names of desktops, cuz those shouldn't be changing during
+ the save.. */
+ savedata->focus_client = focus_client;
+ savedata->desktop = screen_desktop;
+ return savedata;
+}
+
+static void sm_save_yourself_2(SmcConn conn, SmPointer data)
+{
+ gboolean success;
+ ObSMSaveData *savedata = data;
+
+ /* save the current state */
+ ob_debug_type(OB_DEBUG_SM, "Session save phase 2 requested");
+ ob_debug_type(OB_DEBUG_SM,
+ " Saving session to file '%s'", ob_sm_save_file);
+ if (savedata == NULL)
+ savedata = sm_save_get_data();
+ success = session_save_to_file(savedata);
+ g_slice_free(ObSMSaveData, savedata);
+
+ /* tell the session manager how to restore this state */
+ if (success) session_setup_restart_command();
+
+ ob_debug_type(OB_DEBUG_SM, "Saving is done (success = %d)", success);
+ SmcSaveYourselfDone(conn, success);
+}
+
+static void sm_save_yourself(SmcConn conn, SmPointer data, gint save_type,
+ Bool shutdown, gint interact_style, Bool fast)
+{
+ ObSMSaveData *savedata = NULL;
+ gchar *vendor;
+
+#ifdef DEBUG
+ {
+ const gchar *sname =
+ (save_type == SmSaveLocal ? "SmSaveLocal" :
+ (save_type == SmSaveGlobal ? "SmSaveGlobal" :
+ (save_type == SmSaveBoth ? "SmSaveBoth" : "INVALID!!")));
+ ob_debug_type(OB_DEBUG_SM, "Session save requested, type %s", sname);
+ }
+#endif
+
+ if (save_type == SmSaveGlobal) {
+ /* we have no data to save. we only store state to get back to where
+ we were, we don't keep open writable files or anything */
+ SmcSaveYourselfDone(conn, TRUE);
+ return;
+ }
+
+ vendor = SmcVendor(sm_conn);
+ ob_debug_type(OB_DEBUG_SM, "Session manager's vendor: %s", vendor);
+
+ if (!strcmp(vendor, "KDE")) {
+ /* ksmserver guarantees that phase 1 will complete before allowing any
+ clients interaction, so we can save this sanely here before they
+ get messed up from interaction */
+ savedata = sm_save_get_data();
+ }
+ free(vendor);
+
+ if (!SmcRequestSaveYourselfPhase2(conn, sm_save_yourself_2, savedata)) {
+ ob_debug_type(OB_DEBUG_SM, "Requst for phase 2 failed");
+ g_slice_free(ObSMSaveData, savedata);
+ SmcSaveYourselfDone(conn, FALSE);
+ }
+}
+
+static void sm_die(SmcConn conn, SmPointer data)
+{
+ ob_debug_type(OB_DEBUG_SM, "Die requested");
+ ob_exit(0);
+}
+
+static void sm_save_complete(SmcConn conn, SmPointer data)
+{
+ ob_debug_type(OB_DEBUG_SM, "Save complete");
+}
+
+static void sm_shutdown_cancelled(SmcConn conn, SmPointer data)
+{
+ ob_debug_type(OB_DEBUG_SM, "Shutdown cancelled");
+}
+
+static gboolean session_save_to_file(const ObSMSaveData *savedata)
+{
+ FILE *f;
+ GList *it;
+ gboolean success = TRUE;
+
+ f = fopen(ob_sm_save_file, "w");
+ if (!f) {
+ success = FALSE;
+ g_message(_("Unable to save the session to \"%s\": %s"),
+ ob_sm_save_file, g_strerror(errno));
+ } else {
+ fprintf(f, "<?xml version=\"1.0\"?>\n\n");
+ fprintf(f, "<openbox_session>\n\n");
+
+ fprintf(f, "<desktop>%d</desktop>\n", savedata->desktop);
+
+ fprintf(f, "<numdesktops>%d</numdesktops>\n", screen_num_desktops);
+
+ fprintf(f, "<desktoplayout>\n");
+ fprintf(f, " <orientation>%d</orientation>\n",
+ screen_desktop_layout.orientation);
+ fprintf(f, " <startcorner>%d</startcorner>\n",
+ screen_desktop_layout.start_corner);
+ fprintf(f, " <columns>%d</columns>\n",
+ screen_desktop_layout.columns);
+ fprintf(f, " <rows>%d</rows>\n",
+ screen_desktop_layout.rows);
+ fprintf(f, "</desktoplayout>\n");
+
+ if (screen_desktop_names) {
+ gint i;
+ gchar *t;
+
+ fprintf(f, "<desktopnames>\n");
+ for (i = 0; screen_desktop_names[i]; ++i){
+ t = g_markup_escape_text(screen_desktop_names[i], -1);
+ fprintf(f, " <name>%s</name>\n", t);
+ g_free(t);
+ }
+ fprintf(f, "</desktopnames>\n");
+ }
+
+ /* they are ordered top to bottom in stacking order */
+ for (it = stacking_list; it; it = g_list_next(it)) {
+ gint prex, prey, prew, preh;
+ ObClient *c;
+ gchar *t;
+
+ if (WINDOW_IS_CLIENT(it->data))
+ c = WINDOW_AS_CLIENT(it->data);
+ else
+ continue;
+
+ if (!client_normal(c))
+ continue;
+
+ if (!c->sm_client_id) {
+ ob_debug_type(OB_DEBUG_SM, "Client %s does not have a "
+ "session id set",
+ c->title);
+ if (!c->wm_command) {
+ ob_debug_type(OB_DEBUG_SM, "Client %s does not have an "
+ "oldskool wm_command set either. We won't "
+ "be saving its data",
+ c->title);
+ continue;
+ }
+ }
+
+ ob_debug_type(OB_DEBUG_SM, "Saving state for client %s",
+ c->title);
+
+ prex = c->area.x;
+ prey = c->area.y;
+ prew = c->area.width;
+ preh = c->area.height;
+ if (c->fullscreen) {
+ prex = c->pre_fullscreen_area.x;
+ prey = c->pre_fullscreen_area.x;
+ prew = c->pre_fullscreen_area.width;
+ preh = c->pre_fullscreen_area.height;
+ }
+ if (c->max_horz) {
+ prex = c->pre_max_area.x;
+ prew = c->pre_max_area.width;
+ }
+ if (c->max_vert) {
+ prey = c->pre_max_area.y;
+ preh = c->pre_max_area.height;
+ }
+
+ if (c->sm_client_id)
+ fprintf(f, "<window id=\"%s\">\n", c->sm_client_id);
+ else {
+ t = g_markup_escape_text(c->wm_command, -1);
+ fprintf(f, "<window command=\"%s\">\n", t);
+ g_free(t);
+ }
+
+ t = g_markup_escape_text(c->name, -1);
+ fprintf(f, "\t<name>%s</name>\n", t);
+ g_free(t);
+
+ t = g_markup_escape_text(c->class, -1);
+ fprintf(f, "\t<class>%s</class>\n", t);
+ g_free(t);
+
+ t = g_markup_escape_text(c->role, -1);
+ fprintf(f, "\t<role>%s</role>\n", t);
+ g_free(t);
+
+ fprintf(f, "\t<windowtype>%d</windowtype>\n", c->type);
+
+ fprintf(f, "\t<desktop>%d</desktop>\n", c->desktop);
+ fprintf(f, "\t<x>%d</x>\n", prex);
+ fprintf(f, "\t<y>%d</y>\n", prey);
+ fprintf(f, "\t<width>%d</width>\n", prew);
+ fprintf(f, "\t<height>%d</height>\n", preh);
+ if (c->shaded)
+ fprintf(f, "\t<shaded />\n");
+ if (c->iconic)
+ fprintf(f, "\t<iconic />\n");
+ if (c->skip_pager)
+ fprintf(f, "\t<skip_pager />\n");
+ if (c->skip_taskbar)
+ fprintf(f, "\t<skip_taskbar />\n");
+ if (c->fullscreen)
+ fprintf(f, "\t<fullscreen />\n");
+ if (c->above)
+ fprintf(f, "\t<above />\n");
+ if (c->below)
+ fprintf(f, "\t<below />\n");
+ if (c->max_horz)
+ fprintf(f, "\t<max_horz />\n");
+ if (c->max_vert)
+ fprintf(f, "\t<max_vert />\n");
+ if (c->undecorated)
+ fprintf(f, "\t<undecorated />\n");
+ if (savedata->focus_client == c)
+ fprintf(f, "\t<focused />\n");
+ fprintf(f, "</window>\n\n");
+ }
+
+ fprintf(f, "</openbox_session>\n");
+
+ if (fflush(f)) {
+ success = FALSE;
+ g_message(_("Error while saving the session to \"%s\": %s"),
+ ob_sm_save_file, g_strerror(errno));
+ }
+ fclose(f);
+ }
+
+ return success;
+}
+
+static void session_state_free(ObSessionState *state)
+{
+ if (state) {
+ g_free(state->id);
+ g_free(state->command);
+ g_free(state->name);
+ g_free(state->class);
+ g_free(state->role);
+
+ g_slice_free(ObSessionState, state);
+ }
+}
+
+static gboolean session_state_cmp(ObSessionState *s, ObClient *c)
+{
+ ob_debug_type(OB_DEBUG_SM, "Comparing client against saved state: ");
+ ob_debug_type(OB_DEBUG_SM, " client id: %s ", c->sm_client_id);
+ ob_debug_type(OB_DEBUG_SM, " client name: %s ", c->name);
+ ob_debug_type(OB_DEBUG_SM, " client class: %s ", c->class);
+ ob_debug_type(OB_DEBUG_SM, " client role: %s ", c->role);
+ ob_debug_type(OB_DEBUG_SM, " client type: %d ", c->type);
+ ob_debug_type(OB_DEBUG_SM, " client command: %s ",
+ c->wm_command ? c->wm_command : "(null)");
+ ob_debug_type(OB_DEBUG_SM, " state id: %s ", s->id);
+ ob_debug_type(OB_DEBUG_SM, " state name: %s ", s->name);
+ ob_debug_type(OB_DEBUG_SM, " state class: %s ", s->class);
+ ob_debug_type(OB_DEBUG_SM, " state role: %s ", s->role);
+ ob_debug_type(OB_DEBUG_SM, " state type: %d ", s->type);
+ ob_debug_type(OB_DEBUG_SM, " state command: %s ",
+ s->command ? s->command : "(null)");
+
+ if ((c->sm_client_id && s->id && !strcmp(c->sm_client_id, s->id)) ||
+ (c->wm_command && s->command && !strcmp(c->wm_command, s->command)))
+ {
+ return (!strcmp(s->name, c->name) &&
+ !strcmp(s->class, c->class) &&
+ !strcmp(s->role, c->role) &&
+ /* the check for type is to catch broken clients, like
+ firefox, which open a different window on startup
+ with the same info as the one we saved. only do this
+ check for old windows that dont use xsmp, others should
+ know better ! */
+ (!s->command || c->type == s->type));
+ }
+ return FALSE;
+}
+
+GList* session_state_find(ObClient *c)
+{
+ GList *it;
+
+ for (it = session_saved_state; it; it = g_list_next(it)) {
+ ObSessionState *s = it->data;
+ if (!s->matched && session_state_cmp(s, c)) {
+ s->matched = TRUE;
+ break;
+ }
+ }
+ return it;
+}
+
+static void session_load_file(const gchar *path)
+{
+ ObtXmlInst *i;
+ xmlNodePtr node, n, m;
+ GList *it, *inext;
+
+ i = obt_xml_instance_new();
+
+ if (!obt_xml_load_file(i, path, "openbox_session")) {
+ ob_debug_type(OB_DEBUG_SM, "ERROR: session file is missing root node");
+ obt_xml_instance_unref(i);
+ return;
+ }
+ node = obt_xml_root(i);
+
+ if ((n = obt_xml_find_node(node->children, "desktop")))
+ session_desktop = obt_xml_node_int(n);
+
+ if ((n = obt_xml_find_node(node->children, "numdesktops")))
+ session_num_desktops = obt_xml_node_int(n);
+
+ if ((n = obt_xml_find_node(node->children, "desktoplayout"))) {
+ /* make sure they are all there for it to be valid */
+ if ((m = obt_xml_find_node(n->children, "orientation")))
+ session_desktop_layout.orientation = obt_xml_node_int(m);
+ if (m && (m = obt_xml_find_node(n->children, "startcorner")))
+ session_desktop_layout.start_corner = obt_xml_node_int(m);
+ if (m && (m = obt_xml_find_node(n->children, "columns")))
+ session_desktop_layout.columns = obt_xml_node_int(m);
+ if (m && (m = obt_xml_find_node(n->children, "rows")))
+ session_desktop_layout.rows = obt_xml_node_int(m);
+ session_desktop_layout_present = m != NULL;
+ }
+
+ if ((n = obt_xml_find_node(node->children, "desktopnames"))) {
+ for (m = obt_xml_find_node(n->children, "name"); m;
+ m = obt_xml_find_node(m->next, "name"))
+ {
+ session_desktop_names = g_slist_append(session_desktop_names,
+ obt_xml_node_string(m));
+ }
+ }
+
+ ob_debug_type(OB_DEBUG_SM, "loading windows");
+ for (node = obt_xml_find_node(node->children, "window"); node != NULL;
+ node = obt_xml_find_node(node->next, "window"))
+ {
+ ObSessionState *state;
+
+ state = g_slice_new0(ObSessionState);
+
+ if (!obt_xml_attr_string(node, "id", &state->id))
+ if (!obt_xml_attr_string(node, "command", &state->command))
+ goto session_load_bail;
+ if (!(n = obt_xml_find_node(node->children, "name")))
+ goto session_load_bail;
+ state->name = obt_xml_node_string(n);
+ if (!(n = obt_xml_find_node(node->children, "class")))
+ goto session_load_bail;
+ state->class = obt_xml_node_string(n);
+ if (!(n = obt_xml_find_node(node->children, "role")))
+ goto session_load_bail;
+ state->role = obt_xml_node_string(n);
+ if (!(n = obt_xml_find_node(node->children, "windowtype")))
+ goto session_load_bail;
+ state->type = obt_xml_node_int(n);
+ if (!(n = obt_xml_find_node(node->children, "desktop")))
+ goto session_load_bail;
+ state->desktop = obt_xml_node_int(n);
+ if (!(n = obt_xml_find_node(node->children, "x")))
+ goto session_load_bail;
+ state->x = obt_xml_node_int(n);
+ if (!(n = obt_xml_find_node(node->children, "y")))
+ goto session_load_bail;
+ state->y = obt_xml_node_int(n);
+ if (!(n = obt_xml_find_node(node->children, "width")))
+ goto session_load_bail;
+ state->w = obt_xml_node_int(n);
+ if (!(n = obt_xml_find_node(node->children, "height")))
+ goto session_load_bail;
+ state->h = obt_xml_node_int(n);
+
+ state->shaded =
+ obt_xml_find_node(node->children, "shaded") != NULL;
+ state->iconic =
+ obt_xml_find_node(node->children, "iconic") != NULL;
+ state->skip_pager =
+ obt_xml_find_node(node->children, "skip_pager") != NULL;
+ state->skip_taskbar =
+ obt_xml_find_node(node->children, "skip_taskbar") != NULL;
+ state->fullscreen =
+ obt_xml_find_node(node->children, "fullscreen") != NULL;
+ state->above =
+ obt_xml_find_node(node->children, "above") != NULL;
+ state->below =
+ obt_xml_find_node(node->children, "below") != NULL;
+ state->max_horz =
+ obt_xml_find_node(node->children, "max_horz") != NULL;
+ state->max_vert =
+ obt_xml_find_node(node->children, "max_vert") != NULL;
+ state->undecorated =
+ obt_xml_find_node(node->children, "undecorated") != NULL;
+ state->focused =
+ obt_xml_find_node(node->children, "focused") != NULL;
+
+ /* save this. they are in the file in stacking order, so preserve
+ that order here */
+ session_saved_state = g_list_append(session_saved_state, state);
+ ob_debug_type(OB_DEBUG_SM, "loaded %s", state->name);
+ continue;
+
+ session_load_bail:
+ ob_debug_type(OB_DEBUG_SM, "loading FAILED");
+ session_state_free(state);
+ }
+
+ /* Remove any duplicates. This means that if two windows (or more) are
+ saved with the same session state, we won't restore a session for any
+ of them because we don't know what window to put what on. AHEM FIREFOX.
+
+ This is going to be an O(2^n) kind of operation unfortunately.
+ */
+ for (it = session_saved_state; it; it = inext) {
+ GList *jt, *jnext;
+ gboolean founddup = FALSE;
+ ObSessionState *s1 = it->data;
+
+ inext = g_list_next(it);
+
+ for (jt = g_list_next(it); jt; jt = jnext) {
+ ObSessionState *s2 = jt->data;
+ gboolean match;
+
+ jnext = g_list_next(jt);
+
+ if (s1->id && s2->id)
+ match = strcmp(s1->id, s2->id) == 0;
+ else if (s1->command && s2->command)
+ match = strcmp(s1->command, s2->command) == 0;
+ else
+ match = FALSE;
+
+ if (match &&
+ !strcmp(s1->name, s2->name) &&
+ !strcmp(s1->class, s2->class) &&
+ !strcmp(s1->role, s2->role))
+ {
+ ob_debug_type(OB_DEBUG_SM, "removing duplicate %s", s2->name);
+ session_state_free(s2);
+ session_saved_state =
+ g_list_delete_link(session_saved_state, jt);
+ founddup = TRUE;
+ }
+ }
+
+ if (founddup) {
+ ob_debug_type(OB_DEBUG_SM, "removing duplicate %s", s1->name);
+ session_state_free(s1);
+ session_saved_state = g_list_delete_link(session_saved_state, it);
+ }
+ }
+
+ obt_xml_instance_unref(i);
+}
+
+void session_request_logout(gboolean silent)
+{
+ if (sm_conn) {
+ SmcRequestSaveYourself(sm_conn,
+ SmSaveGlobal,
+ TRUE, /* logout */
+ (silent ?
+ SmInteractStyleNone : SmInteractStyleAny),
+ TRUE, /* if false, with GSM, it shows the old
+ logout prompt */
+ TRUE); /* global */
+ }
+ else
+ g_message(_("Not connected to a session manager"));
+}
+
+#endif
diff --git a/openbox/session.h b/openbox/session.h
new file mode 100644
index 0000000..f37e211
--- /dev/null
+++ b/openbox/session.h
@@ -0,0 +1,60 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ session.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __ob__session_h
+#define __ob__session_h
+
+#include "client.h"
+#include "screen.h"
+
+#include <glib.h>
+
+typedef struct _ObSessionState ObSessionState;
+
+struct _ObSessionState {
+ gchar *id, *command, *name, *class, *role;
+ ObClientType type;
+ guint desktop;
+ gint x, y, w, h;
+ gboolean shaded, iconic, skip_pager, skip_taskbar, fullscreen;
+ gboolean above, below, max_horz, max_vert, undecorated;
+ gboolean focused;
+
+ gboolean matched;
+};
+
+/*! The desktop being viewed when the session was saved. A valud of -1 means
+ it was not saved */
+extern gint session_desktop;
+extern gint session_num_desktops;
+extern gboolean session_desktop_layout_present;
+extern ObDesktopLayout session_desktop_layout;
+extern GSList *session_desktop_names;
+
+extern GList *session_saved_state;
+
+void session_startup(gint argc, gchar **argv);
+void session_shutdown(gboolean permanent);
+
+GList* session_state_find(struct _ObClient *c);
+
+void session_request_logout(gboolean silent);
+
+gboolean session_connected(void);
+
+#endif
diff --git a/openbox/stacking.c b/openbox/stacking.c
new file mode 100644
index 0000000..58551b5
--- /dev/null
+++ b/openbox/stacking.c
@@ -0,0 +1,710 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ stacking.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "openbox.h"
+#include "screen.h"
+#include "focus.h"
+#include "client.h"
+#include "group.h"
+#include "frame.h"
+#include "window.h"
+#include "event.h"
+#include "debug.h"
+#include "obt/prop.h"
+
+GList *stacking_list = NULL;
+GList *stacking_list_tail = NULL;
+/*! When true, stacking changes will not be reflected on the screen. This is
+ to freeze the on-screen stacking order while a window is being temporarily
+ raised during focus cycling */
+static gboolean pause_changes = FALSE;
+
+void stacking_set_list(void)
+{
+ Window *windows = NULL;
+ GList *it;
+ guint i = 0;
+
+ /* on shutdown, don't update the properties, so that we can read it back
+ in on startup and re-stack the windows as they were before we shut down
+ */
+ if (ob_state() == OB_STATE_EXITING) return;
+
+ /* create an array of the window ids (from bottom to top,
+ reverse order!) */
+ if (stacking_list) {
+ windows = g_new(Window, g_list_length(stacking_list));
+ for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
+ if (WINDOW_IS_CLIENT(it->data))
+ windows[i++] = WINDOW_AS_CLIENT(it->data)->window;
+ }
+ }
+
+ OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST_STACKING, WINDOW,
+ (gulong*)windows, i);
+
+ g_free(windows);
+}
+
+static void do_restack(GList *wins, GList *before)
+{
+ GList *it;
+ Window *win;
+ gint i;
+
+#ifdef DEBUG
+ GList *next;
+
+ g_assert(wins);
+ /* pls only restack stuff in the same layer at a time */
+ for (it = wins; it; it = next) {
+ next = g_list_next(it);
+ if (!next) break;
+ g_assert (window_layer(it->data) == window_layer(next->data));
+ }
+ if (before)
+ g_assert(window_layer(it->data) >= window_layer(before->data));
+#endif
+
+ win = g_new(Window, g_list_length(wins) + 1);
+
+ if (before == stacking_list)
+ win[0] = screen_support_win;
+ else if (!before)
+ win[0] = window_top(g_list_last(stacking_list)->data);
+ else
+ win[0] = window_top(g_list_previous(before)->data);
+
+ for (i = 1, it = wins; it; ++i, it = g_list_next(it)) {
+ win[i] = window_top(it->data);
+ g_assert(win[i] != None); /* better not call stacking shit before
+ setting your top level window value */
+ stacking_list = g_list_insert_before(stacking_list, before, it->data);
+ }
+
+#ifdef DEBUG
+ /* some debug checking of the stacking list's order */
+ for (it = stacking_list; ; it = next) {
+ next = g_list_next(it);
+ if (!next) break;
+ g_assert(window_layer(it->data) >= window_layer(next->data));
+ }
+#endif
+
+ if (!pause_changes)
+ XRestackWindows(obt_display, win, i);
+ g_free(win);
+
+ stacking_set_list();
+}
+
+void stacking_temp_raise(ObWindow *window)
+{
+ Window win[2];
+ GList *it;
+ gulong start;
+
+ /* don't use this for internal windows..! it would lower them.. */
+ g_assert(window_layer(window) < OB_STACKING_LAYER_INTERNAL);
+
+ /* find the window to drop it underneath */
+ win[0] = screen_support_win;
+ for (it = stacking_list; it; it = g_list_next(it)) {
+ ObWindow *w = it->data;
+ if (window_layer(w) >= OB_STACKING_LAYER_INTERNAL)
+ win[0] = window_top(w);
+ else
+ break;
+ }
+
+ win[1] = window_top(window);
+ start = event_start_ignore_all_enters();
+ XRestackWindows(obt_display, win, 2);
+ event_end_ignore_all_enters(start);
+
+ pause_changes = TRUE;
+}
+
+void stacking_restore(void)
+{
+ Window *win;
+ GList *it;
+ gint i;
+ gulong start;
+
+ win = g_new(Window, g_list_length(stacking_list) + 1);
+ win[0] = screen_support_win;
+ for (i = 1, it = stacking_list; it; ++i, it = g_list_next(it))
+ win[i] = window_top(it->data);
+ start = event_start_ignore_all_enters();
+ XRestackWindows(obt_display, win, i);
+ event_end_ignore_all_enters(start);
+ g_free(win);
+
+ pause_changes = FALSE;
+}
+
+static void do_raise(GList *wins)
+{
+ GList *it;
+ GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
+ gint i;
+
+ for (it = wins; it; it = g_list_next(it)) {
+ ObStackingLayer l;
+
+ l = window_layer(it->data);
+ layer[l] = g_list_append(layer[l], it->data);
+ }
+
+ it = stacking_list;
+ for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
+ if (layer[i]) {
+ for (; it; it = g_list_next(it)) {
+ /* look for the top of the layer */
+ if (window_layer(it->data) <= (ObStackingLayer) i)
+ break;
+ }
+ do_restack(layer[i], it);
+ g_list_free(layer[i]);
+ }
+ }
+}
+
+static void do_lower(GList *wins)
+{
+ GList *it;
+ GList *layer[OB_NUM_STACKING_LAYERS] = {NULL};
+ gint i;
+
+ for (it = wins; it; it = g_list_next(it)) {
+ ObStackingLayer l;
+
+ l = window_layer(it->data);
+ layer[l] = g_list_append(layer[l], it->data);
+ }
+
+ it = stacking_list;
+ for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) {
+ if (layer[i]) {
+ for (; it; it = g_list_next(it)) {
+ /* look for the top of the next layer down */
+ if (window_layer(it->data) < (ObStackingLayer) i)
+ break;
+ }
+ do_restack(layer[i], it);
+ g_list_free(layer[i]);
+ }
+ }
+}
+
+static void restack_windows(ObClient *selected, gboolean raise)
+{
+ GList *it, *last, *below, *above, *next;
+ GList *wins = NULL;
+
+ GList *group_helpers = NULL;
+ GList *group_modals = NULL;
+ GList *group_trans = NULL;
+ GList *modals = NULL;
+ GList *trans = NULL;
+
+ if (raise) {
+ ObClient *p;
+
+ /* if a window is modal for another single window, then raise it to the
+ top too, the same is done with the focus order */
+ while (selected->modal && (p = client_direct_parent(selected)))
+ selected = p;
+ }
+
+ /* remove first so we can't run into ourself */
+ it = g_list_find(stacking_list, selected);
+ g_assert(it);
+ stacking_list = g_list_delete_link(stacking_list, it);
+
+ /* go from the bottom of the stacking list up. don't move any other windows
+ when lowering, we call this for each window independently */
+ if (raise) {
+ for (it = g_list_last(stacking_list); it; it = next) {
+ next = g_list_previous(it);
+
+ if (WINDOW_IS_CLIENT(it->data)) {
+ ObClient *ch = it->data;
+
+ /* only move windows in the same stacking layer */
+ if (ch->layer == selected->layer &&
+ /* looking for windows that are transients, and so would
+ remain above the selected window */
+ client_search_transient(selected, ch))
+ {
+ if (client_is_direct_child(selected, ch)) {
+ if (ch->modal)
+ modals = g_list_prepend(modals, ch);
+ else
+ trans = g_list_prepend(trans, ch);
+ }
+ else if (client_helper(ch)) {
+ if (selected->transient) {
+ /* helpers do not stay above transient windows */
+ continue;
+ }
+ group_helpers = g_list_prepend(group_helpers, ch);
+ }
+ else {
+ if (ch->modal)
+ group_modals = g_list_prepend(group_modals, ch);
+ else
+ group_trans = g_list_prepend(group_trans, ch);
+ }
+ stacking_list = g_list_delete_link(stacking_list, it);
+ }
+ }
+ }
+ }
+
+ /* put modals above other direct transients */
+ wins = g_list_concat(modals, trans);
+
+ /* put helpers below direct transients */
+ wins = g_list_concat(wins, group_helpers);
+
+ /* put the selected window right below these children */
+ wins = g_list_append(wins, selected);
+
+ /* if selected window is transient for group then raise it above others */
+ if (selected->transient_for_group) {
+ /* if it's modal, raise it above those also */
+ if (selected->modal) {
+ wins = g_list_concat(wins, group_modals);
+ group_modals = NULL;
+ }
+ wins = g_list_concat(wins, group_trans);
+ group_trans = NULL;
+ }
+
+ /* find where to put the selected window, start from bottom of list,
+ this is the window below everything we are re-adding to the list */
+ last = NULL;
+ for (it = g_list_last(stacking_list); it; it = g_list_previous(it))
+ {
+ if (window_layer(it->data) < selected->layer) {
+ last = it;
+ continue;
+ }
+ /* if lowering, stop at the beginning of the layer */
+ if (!raise)
+ break;
+ /* if raising, stop at the end of the layer */
+ if (window_layer(it->data) > selected->layer)
+ break;
+
+ last = it;
+ }
+
+ /* save this position in the stacking list */
+ below = last;
+
+ /* find where to put the group transients, start from the top of list */
+ for (it = stacking_list; it; it = g_list_next(it)) {
+ /* skip past higher layers */
+ if (window_layer(it->data) > selected->layer)
+ continue;
+ /* if we reach the end of the layer (how?) then don't go further */
+ if (window_layer(it->data) < selected->layer)
+ break;
+ /* stop when we reach the first window in the group */
+ if (WINDOW_IS_CLIENT(it->data)) {
+ ObClient *c = it->data;
+ if (c->group == selected->group)
+ break;
+ }
+ /* if we don't hit any other group members, stop here because this
+ is where we are putting the selected window (and its children) */
+ if (it == below)
+ break;
+ }
+
+ /* save this position, this is the top of the group of windows between the
+ group transient ones we're restacking and the others up above that we're
+ restacking
+
+ we actually want to save 1 position _above_ that, for for loops to work
+ nicely, so move back one position in the list while saving it
+ */
+ above = it ? g_list_previous(it) : g_list_last(stacking_list);
+
+ /* put the windows inside the gap to the other windows we're stacking
+ into the restacking list, go from the bottom up so that we can use
+ g_list_prepend */
+ if (below) it = g_list_previous(below);
+ else it = g_list_last(stacking_list);
+ for (; it != above; it = next) {
+ next = g_list_previous(it);
+ wins = g_list_prepend(wins, it->data);
+ stacking_list = g_list_delete_link(stacking_list, it);
+ }
+
+ /* group transients go above the rest of the stuff acquired to now */
+ wins = g_list_concat(group_trans, wins);
+ /* group modals go on the very top */
+ wins = g_list_concat(group_modals, wins);
+
+ do_restack(wins, below);
+ g_list_free(wins);
+
+ /* lower our parents after us, so they go below us */
+ if (!raise && selected->parents) {
+ GSList *parents_copy, *sit;
+ GSList *reorder = NULL;
+
+ parents_copy = g_slist_copy(selected->parents);
+
+ /* go thru stacking list backwards so we can use g_slist_prepend */
+ for (it = g_list_last(stacking_list); it && parents_copy;
+ it = g_list_previous(it))
+ if ((sit = g_slist_find(parents_copy, it->data))) {
+ reorder = g_slist_prepend(reorder, sit->data);
+ parents_copy = g_slist_delete_link(parents_copy, sit);
+ }
+ g_assert(parents_copy == NULL);
+
+ /* call restack for each of these to lower them */
+ for (sit = reorder; sit; sit = g_slist_next(sit))
+ restack_windows(sit->data, raise);
+ }
+}
+
+void stacking_raise(ObWindow *window)
+{
+ if (WINDOW_IS_CLIENT(window)) {
+ ObClient *selected;
+ selected = WINDOW_AS_CLIENT(window);
+ restack_windows(selected, TRUE);
+ } else {
+ GList *wins;
+ wins = g_list_append(NULL, window);
+ stacking_list = g_list_remove(stacking_list, window);
+ do_raise(wins);
+ g_list_free(wins);
+ }
+ stacking_list_tail = g_list_last(stacking_list);
+}
+
+void stacking_lower(ObWindow *window)
+{
+ if (WINDOW_IS_CLIENT(window)) {
+ ObClient *selected;
+ selected = WINDOW_AS_CLIENT(window);
+ restack_windows(selected, FALSE);
+ } else {
+ GList *wins;
+ wins = g_list_append(NULL, window);
+ stacking_list = g_list_remove(stacking_list, window);
+ do_lower(wins);
+ g_list_free(wins);
+ }
+ stacking_list_tail = g_list_last(stacking_list);
+}
+
+void stacking_below(ObWindow *window, ObWindow *below)
+{
+ GList *wins, *before;
+
+ if (window_layer(window) != window_layer(below))
+ return;
+
+ wins = g_list_append(NULL, window);
+ stacking_list = g_list_remove(stacking_list, window);
+ before = g_list_next(g_list_find(stacking_list, below));
+ do_restack(wins, before);
+ g_list_free(wins);
+ stacking_list_tail = g_list_last(stacking_list);
+}
+
+void stacking_add(ObWindow *win)
+{
+ g_assert(screen_support_win != None); /* make sure I dont break this in the
+ future */
+ /* don't add windows that are being unmanaged ! */
+ if (WINDOW_IS_CLIENT(win)) g_assert(WINDOW_AS_CLIENT(win)->managed);
+
+ stacking_list = g_list_append(stacking_list, win);
+
+ stacking_raise(win);
+ /* stacking_list_tail set by stacking_raise() */
+}
+
+static GList *find_highest_relative(ObClient *client)
+{
+ GList *ret = NULL;
+
+ if (client->parents) {
+ GList *it;
+ GSList *top;
+
+ /* get all top level relatives of this client */
+ top = client_search_all_top_parents_layer(client);
+
+ /* go from the top of the stacking order down */
+ for (it = stacking_list; !ret && it; it = g_list_next(it)) {
+ if (WINDOW_IS_CLIENT(it->data)) {
+ ObClient *c = it->data;
+ /* only look at windows in the same layer and that are
+ visible */
+ if (c->layer == client->layer &&
+ !c->iconic &&
+ (c->desktop == client->desktop ||
+ c->desktop == DESKTOP_ALL ||
+ client->desktop == DESKTOP_ALL))
+ {
+ GSList *sit;
+
+ /* go through each top level parent and see it this window
+ is related to them */
+ for (sit = top; !ret && sit; sit = g_slist_next(sit)) {
+ ObClient *topc = sit->data;
+
+ /* are they related ? */
+ if (topc == c || client_search_transient(topc, c))
+ ret = it;
+ }
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+void stacking_add_nonintrusive(ObWindow *win)
+{
+ ObClient *client;
+ GList *it_below = NULL; /* this client will be below us */
+ GList *it_above;
+ GList *wins;
+
+ if (!WINDOW_IS_CLIENT(win)) {
+ stacking_add(win); /* no special rules for others */
+ return;
+ }
+
+ client = WINDOW_AS_CLIENT(win);
+
+ /* don't add windows that are being unmanaged ! */
+ g_assert(client->managed);
+
+ /* insert above its highest parent (or its highest child !) */
+ it_below = find_highest_relative(client);
+
+ if (!it_below) {
+ /* nothing to put it directly above, so try find the focused client
+ to put it underneath it */
+ if (focus_client && client != focus_client &&
+ focus_client->layer == client->layer)
+ {
+ it_below = g_list_find(stacking_list, focus_client);
+ /* this can give NULL, but it means the focused window is on the
+ bottom of the stacking order, so go to the bottom in that case,
+ below it */
+ it_below = g_list_next(it_below);
+ }
+ else {
+ /* There is no window to put this directly above, so put it at the
+ top, so you know it is there.
+
+ It used to do this only if the window was focused and lower
+ it otherwise.
+
+ We also put it at the top not the bottom to fix a bug with
+ fullscreen windows. When focusLast is off and followsMouse is
+ on, when you switch desktops, the fullscreen window loses
+ focus and goes into its lower layer. If this puts it at the
+ bottom then when you come back to the desktop, the window is
+ at the bottom and won't get focus back.
+ */
+ it_below = stacking_list;
+ }
+ }
+
+ /* make sure it's not in the wrong layer though ! */
+ for (; it_below; it_below = g_list_next(it_below)) {
+ /* stop when the window is not in a higher layer than the window
+ it is going above (it_below) */
+ if (client->layer >= window_layer(it_below->data))
+ break;
+ }
+ for (; it_below != stacking_list; it_below = it_above) {
+ /* stop when the window is not in a lower layer than the
+ window it is going under (it_above) */
+ it_above = it_below ?
+ g_list_previous(it_below) : g_list_last(stacking_list);
+ if (client->layer <= window_layer(it_above->data))
+ break;
+ }
+
+ wins = g_list_append(NULL, win);
+ do_restack(wins, it_below);
+ g_list_free(wins);
+ stacking_list_tail = g_list_last(stacking_list);
+}
+
+/*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it
+ tries against all other clients.
+*/
+static gboolean stacking_occluded(ObClient *client, ObClient *sibling)
+{
+ GList *it;
+ gboolean occluded = FALSE;
+
+ /* no need for any looping in this case */
+ if (sibling && client->layer != sibling->layer)
+ return occluded;
+
+ for (it = g_list_previous(g_list_find(stacking_list, client)); it;
+ it = g_list_previous(it))
+ if (WINDOW_IS_CLIENT(it->data)) {
+ ObClient *c = it->data;
+ if (!c->iconic &&
+ (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
+ c->desktop == client->desktop) &&
+ !client_search_transient(client, c))
+ {
+ if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
+ {
+ if (sibling != NULL) {
+ if (c == sibling) {
+ occluded = TRUE;
+ break;
+ }
+ }
+ else if (c->layer == client->layer) {
+ occluded = TRUE;
+ break;
+ }
+ else if (c->layer > client->layer)
+ break; /* we past its layer */
+ }
+ }
+ }
+ return occluded;
+}
+
+/*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries
+ against all other clients.
+*/
+static gboolean stacking_occludes(ObClient *client, ObClient *sibling)
+{
+ GList *it;
+ gboolean occludes = FALSE;
+
+ /* no need for any looping in this case */
+ if (sibling && client->layer != sibling->layer)
+ return occludes;
+
+ for (it = g_list_next(g_list_find(stacking_list, client));
+ it; it = g_list_next(it))
+ if (WINDOW_IS_CLIENT(it->data)) {
+ ObClient *c = it->data;
+ if (!c->iconic &&
+ (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL ||
+ c->desktop == client->desktop) &&
+ !client_search_transient(c, client))
+ {
+ if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area))
+ {
+ if (sibling != NULL) {
+ if (c == sibling) {
+ occludes = TRUE;
+ break;
+ }
+ }
+ else if (c->layer == client->layer) {
+ occludes = TRUE;
+ break;
+ }
+ else if (c->layer < client->layer)
+ break; /* we past its layer */
+ }
+ }
+ }
+ return occludes;
+}
+
+gboolean stacking_restack_request(ObClient *client, ObClient *sibling,
+ gint detail)
+{
+ gboolean ret = FALSE;
+
+ if (sibling && ((client->desktop != sibling->desktop &&
+ client->desktop != DESKTOP_ALL &&
+ sibling->desktop != DESKTOP_ALL) ||
+ sibling->iconic))
+ {
+ ob_debug("Setting restack sibling to NULL, they are not on the same "
+ "desktop or it is iconified");
+ sibling = NULL;
+ }
+
+ switch (detail) {
+ case Below:
+ ob_debug("Restack request Below for client %s sibling %s",
+ client->title, sibling ? sibling->title : "(all)");
+ /* just lower it */
+ stacking_lower(CLIENT_AS_WINDOW(client));
+ ret = TRUE;
+ break;
+ case BottomIf:
+ ob_debug("Restack request BottomIf for client %s sibling %s",
+ client->title, sibling ? sibling->title : "(all)");
+ /* if this client occludes sibling (or anything if NULL), then
+ lower it to the bottom */
+ if (stacking_occludes(client, sibling)) {
+ stacking_lower(CLIENT_AS_WINDOW(client));
+ ret = TRUE;
+ }
+ break;
+ case Above:
+ ob_debug("Restack request Above for client %s sibling %s",
+ client->title, sibling ? sibling->title : "(all)");
+ stacking_raise(CLIENT_AS_WINDOW(client));
+ ret = TRUE;
+ break;
+ case TopIf:
+ ob_debug("Restack request TopIf for client %s sibling %s",
+ client->title, sibling ? sibling->title : "(all)");
+ if (stacking_occluded(client, sibling)) {
+ stacking_raise(CLIENT_AS_WINDOW(client));
+ ret = TRUE;
+ }
+ break;
+ case Opposite:
+ ob_debug("Restack request Opposite for client %s sibling %s",
+ client->title, sibling ? sibling->title : "(all)");
+ if (stacking_occluded(client, sibling)) {
+ stacking_raise(CLIENT_AS_WINDOW(client));
+ ret = TRUE;
+ }
+ else if (stacking_occludes(client, sibling)) {
+ stacking_lower(CLIENT_AS_WINDOW(client));
+ ret = TRUE;
+ }
+ break;
+ }
+ return ret;
+}
diff --git a/openbox/stacking.h b/openbox/stacking.h
new file mode 100644
index 0000000..c14aa2e
--- /dev/null
+++ b/openbox/stacking.h
@@ -0,0 +1,85 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ stacking.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __stacking_h
+#define __stacking_h
+
+#include <glib.h>
+#include <X11/Xlib.h>
+
+struct _ObWindow;
+struct _ObClient;
+
+/*! The possible stacking layers a client window can be a part of */
+typedef enum {
+ OB_STACKING_LAYER_INVALID,
+ OB_STACKING_LAYER_DESKTOP, /*!< 0 - desktop windows */
+ OB_STACKING_LAYER_BELOW, /*!< 1 - normal windows w/ below */
+ OB_STACKING_LAYER_NORMAL, /*!< 2 - normal windows */
+ OB_STACKING_LAYER_ABOVE, /*!< 3 - normal windows w/ above */
+ OB_STACKING_LAYER_FULLSCREEN, /*!< 4 - fullscreeen windows */
+ OB_STACKING_LAYER_INTERNAL, /*!< 5 - openbox windows/menus */
+ OB_NUM_STACKING_LAYERS
+} ObStackingLayer;
+
+/* list of ObWindow*s in stacking order from highest to lowest */
+extern GList *stacking_list;
+/* list of ObWindow*s in stacking order from lowest to highest */
+extern GList *stacking_list_tail;
+
+/*! Sets the window stacking list on the root window from the
+ stacking_list */
+void stacking_set_list(void);
+
+void stacking_add(struct _ObWindow *win);
+void stacking_add_nonintrusive(struct _ObWindow *win);
+#define stacking_remove(win) stacking_list = g_list_remove(stacking_list, win);
+
+/*! Raises a window above all others in its stacking layer */
+void stacking_raise(struct _ObWindow *window);
+
+/*! Temporarily raises a window above all others */
+void stacking_temp_raise(struct _ObWindow *window);
+
+/*! Restores any temporarily raised windows to their correct place */
+void stacking_restore(void);
+
+/*! Lowers a window below all others in its stacking layer */
+void stacking_lower(struct _ObWindow *window);
+
+/*! Moves a window below another if its in the same layer.
+ This function does not enforce stacking rules IRT transients n such, and so
+ it should really ONLY be used to restore stacking orders from saved sessions
+*/
+void stacking_below(struct _ObWindow *window, struct _ObWindow *below);
+
+/*! Restack a window based upon a sibling (or all windows) in various ways.
+ @param client The client to be restacked
+ @param sibling Another client to compare to, or NULL to compare to all
+ windows
+ @param detail One of Above, Below, TopIf, BottomIf, Opposite
+ @return TRUE if the client was restacked
+ See http://tronche.com/gui/x/xlib/window/configure.html for details on
+ how each detail works with and without a sibling.
+*/
+gboolean stacking_restack_request(struct _ObClient *client,
+ struct _ObClient *sibling,
+ gint detail);
+
+#endif
diff --git a/openbox/startupnotify.c b/openbox/startupnotify.c
new file mode 100644
index 0000000..e249002
--- /dev/null
+++ b/openbox/startupnotify.c
@@ -0,0 +1,276 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ startupnotify.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "startupnotify.h"
+#include "gettext.h"
+#include "event.h"
+#include "obt/xqueue.h"
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#ifndef USE_LIBSN
+
+void sn_startup(gboolean reconfig) {}
+void sn_shutdown(gboolean reconfig) {}
+gboolean sn_app_starting() { return FALSE; }
+Time sn_app_started(const gchar *id, const gchar *wmclass, const gchar *name)
+{
+ return CurrentTime;
+}
+gboolean sn_get_desktop(gchar *id, guint *desktop) { return FALSE; }
+void sn_setup_spawn_environment(const gchar *program, const gchar *name,
+ const gchar *icon_name, const gchar *wmclass,
+ gint desktop) {}
+void sn_spawn_cancel() {}
+
+#else
+
+#include "openbox.h"
+#include "screen.h"
+
+#define SN_API_NOT_YET_FROZEN
+#include <libsn/sn.h>
+
+static SnDisplay *sn_display;
+static SnMonitorContext *sn_context;
+static SnLauncherContext *sn_launcher;
+static GSList *sn_waits; /* list of SnStartupSequences we're waiting on */
+
+static SnStartupSequence* sequence_find(const gchar *id);
+
+static void sn_handler(const XEvent *e, gpointer data);
+static void sn_event_func(SnMonitorEvent *event, gpointer data);
+
+void sn_startup(gboolean reconfig)
+{
+ if (reconfig) return;
+
+ sn_display = sn_display_new(obt_display, NULL, NULL);
+ sn_context = sn_monitor_context_new(sn_display, ob_screen,
+ sn_event_func, NULL, NULL);
+ sn_launcher = sn_launcher_context_new(sn_display, ob_screen);
+
+ xqueue_add_callback(sn_handler, NULL);
+}
+
+void sn_shutdown(gboolean reconfig)
+{
+ GSList *it;
+
+ if (reconfig) return;
+
+ xqueue_remove_callback(sn_handler, NULL);
+
+ for (it = sn_waits; it; it = g_slist_next(it))
+ sn_startup_sequence_unref((SnStartupSequence*)it->data);
+ g_slist_free(sn_waits);
+ sn_waits = NULL;
+
+ screen_set_root_cursor();
+
+ sn_launcher_context_unref(sn_launcher);
+ sn_monitor_context_unref(sn_context);
+ sn_display_unref(sn_display);
+}
+
+static SnStartupSequence* sequence_find(const gchar *id)
+{
+ SnStartupSequence*ret = NULL;
+ GSList *it;
+
+ for (it = sn_waits; it; it = g_slist_next(it)) {
+ SnStartupSequence *seq = it->data;
+ if (!strcmp(id, sn_startup_sequence_get_id(seq))) {
+ ret = seq;
+ break;
+ }
+ }
+ return ret;
+}
+
+gboolean sn_app_starting(void)
+{
+ return sn_waits != NULL;
+}
+
+static gboolean sn_wait_timeout(gpointer data)
+{
+ SnStartupSequence *seq = data;
+ sn_waits = g_slist_remove(sn_waits, seq);
+ screen_set_root_cursor();
+ return FALSE; /* don't repeat */
+}
+
+static void sn_handler(const XEvent *e, gpointer data)
+{
+ XEvent ec;
+ ec = *e;
+ sn_display_process_event(sn_display, &ec);
+}
+
+static void sn_event_func(SnMonitorEvent *ev, gpointer data)
+{
+ SnStartupSequence *seq;
+ gboolean change = FALSE;
+
+ if (!(seq = sn_monitor_event_get_startup_sequence(ev)))
+ return;
+
+ switch (sn_monitor_event_get_type(ev)) {
+ case SN_MONITOR_EVENT_INITIATED:
+ sn_startup_sequence_ref(seq);
+ sn_waits = g_slist_prepend(sn_waits, seq);
+ /* 20 second timeout for apps to start if the launcher doesn't
+ have a timeout */
+ g_timeout_add_full(G_PRIORITY_DEFAULT,
+ 20 * 1000, sn_wait_timeout, seq,
+ (GDestroyNotify)sn_startup_sequence_unref);
+ change = TRUE;
+ break;
+ case SN_MONITOR_EVENT_CHANGED:
+ /* XXX feedback changed? */
+ change = TRUE;
+ break;
+ case SN_MONITOR_EVENT_COMPLETED:
+ case SN_MONITOR_EVENT_CANCELED:
+ if ((seq = sequence_find(sn_startup_sequence_get_id(seq)))) {
+ sn_waits = g_slist_remove(sn_waits, seq);
+ g_source_remove_by_user_data(seq);
+ change = TRUE;
+ }
+ break;
+ };
+
+ if (change)
+ screen_set_root_cursor();
+}
+
+Time sn_app_started(const gchar *id, const gchar *wmclass, const gchar *name)
+{
+ GSList *it;
+ Time t = CurrentTime;
+
+ if (!id && !wmclass)
+ return t;
+
+ for (it = sn_waits; it; it = g_slist_next(it)) {
+ SnStartupSequence *seq = it->data;
+ gboolean found = FALSE;
+ const gchar *seqid, *seqclass, *seqbin;
+ seqid = sn_startup_sequence_get_id(seq);
+ seqclass = sn_startup_sequence_get_wmclass(seq);
+ seqbin = sn_startup_sequence_get_binary_name(seq);
+
+ if (id && seqid) {
+ /* if the app has a startup id, then look for that for highest
+ accuracy */
+ if (!strcmp(seqid, id))
+ found = TRUE;
+ }
+ else if (seqclass) {
+ /* seqclass = "a string to match against the "resource name" or
+ "resource class" hints. These are WM_CLASS[0] and WM_CLASS[1]"
+ - from the startup-notification spec
+ */
+ found = (seqclass && !strcmp(seqclass, wmclass)) ||
+ (seqclass && !strcmp(seqclass, name));
+ }
+ else if (seqbin) {
+ /* Check the binary name against the class and name hints
+ as well, to help apps that don't have the class set
+ correctly */
+ found = (seqbin && !g_ascii_strcasecmp(seqbin, wmclass)) ||
+ (seqbin && !g_ascii_strcasecmp(seqbin, name));
+ }
+
+ if (found) {
+ sn_startup_sequence_complete(seq);
+ t = sn_startup_sequence_get_timestamp(seq);
+ break;
+ }
+ }
+ return t;
+}
+
+gboolean sn_get_desktop(gchar *id, guint *desktop)
+{
+ SnStartupSequence *seq;
+
+ if (id && (seq = sequence_find(id))) {
+ gint desk = sn_startup_sequence_get_workspace(seq);
+ if (desk != -1) {
+ *desktop = desk;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static gboolean sn_launch_wait_timeout(gpointer data)
+{
+ SnLauncherContext *sn = data;
+ sn_launcher_context_complete(sn);
+ return FALSE; /* don't repeat */
+}
+
+void sn_setup_spawn_environment(const gchar *program, const gchar *name,
+ const gchar *icon_name, const gchar *wmclass,
+ gint desktop)
+{
+ gchar *desc;
+ const char *id;
+
+ desc = g_strdup_printf(_("Running %s"), program);
+
+ if (sn_launcher_context_get_initiated(sn_launcher)) {
+ sn_launcher_context_unref(sn_launcher);
+ sn_launcher = sn_launcher_context_new(sn_display, ob_screen);
+ }
+
+ sn_launcher_context_set_name(sn_launcher, name ? name : program);
+ sn_launcher_context_set_description(sn_launcher, desc);
+ sn_launcher_context_set_icon_name(sn_launcher, icon_name ?
+ icon_name : program);
+ sn_launcher_context_set_binary_name(sn_launcher, program);
+ if (wmclass) sn_launcher_context_set_wmclass(sn_launcher, wmclass);
+ if (desktop >= 0 && (unsigned) desktop < screen_num_desktops)
+ sn_launcher_context_set_workspace(sn_launcher, (signed) desktop);
+ sn_launcher_context_initiate(sn_launcher, "openbox", program,
+ event_time());
+ id = sn_launcher_context_get_startup_id(sn_launcher);
+
+ /* 20 second timeout for apps to start */
+ sn_launcher_context_ref(sn_launcher);
+ g_timeout_add_full(G_PRIORITY_DEFAULT,
+ 20 * 1000, sn_launch_wait_timeout, sn_launcher,
+ (GDestroyNotify)sn_launcher_context_unref);
+
+ g_setenv("DESKTOP_STARTUP_ID", id, TRUE);
+
+ g_free(desc);
+}
+
+void sn_spawn_cancel(void)
+{
+ sn_launcher_context_complete(sn_launcher);
+}
+
+#endif
diff --git a/openbox/startupnotify.h b/openbox/startupnotify.h
new file mode 100644
index 0000000..d524bca
--- /dev/null
+++ b/openbox/startupnotify.h
@@ -0,0 +1,50 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ startupnotify.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef ob__startupnotify_h
+#define ob__startupnotify_h
+
+#include <glib.h>
+#include <X11/Xlib.h>
+
+void sn_startup(gboolean reconfig);
+void sn_shutdown(gboolean reconfig);
+
+gboolean sn_app_starting(void);
+
+/*! Notify that an app has started
+ @param wmclass the WM_CLASS[1] hint
+ @param name the WM_CLASS[0] hint
+ */
+Time sn_app_started(const gchar *id, const gchar *wmclass, const gchar *name);
+
+/*! Get the desktop requested via the startup-notiication protocol if one
+ was requested */
+gboolean sn_get_desktop(gchar *id, guint *desktop);
+
+/* Get the environment to run the program in, with startup notification */
+void sn_setup_spawn_environment(const gchar *program, const gchar *name,
+ const gchar *icon_name, const gchar *wmclass,
+ gint desktop);
+
+/* Tell startup notification we're not actually running the program we
+ told it we were
+*/
+void sn_spawn_cancel(void);
+
+#endif
diff --git a/openbox/translate.c b/openbox/translate.c
new file mode 100644
index 0000000..50d5000
--- /dev/null
+++ b/openbox/translate.c
@@ -0,0 +1,160 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ translate.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "openbox.h"
+#include "mouse.h"
+#include "gettext.h"
+#include "obt/keyboard.h"
+
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+
+static guint translate_modifier(gchar *str)
+{
+ guint mask = 0;
+
+ if (!g_ascii_strcasecmp("Mod1", str)) mask = Mod1Mask;
+ else if (!g_ascii_strcasecmp("Mod2", str)) mask = Mod2Mask;
+ else if (!g_ascii_strcasecmp("Mod3", str)) mask = Mod3Mask;
+ else if (!g_ascii_strcasecmp("Mod4", str)) mask = Mod4Mask;
+ else if (!g_ascii_strcasecmp("Mod5", str)) mask = Mod5Mask;
+
+ else if (!g_ascii_strcasecmp("Control", str) ||
+ !g_ascii_strcasecmp("C", str))
+ mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL);
+ else if (!g_ascii_strcasecmp("Alt", str) ||
+ !g_ascii_strcasecmp("A", str))
+ mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_ALT);
+ else if (!g_ascii_strcasecmp("Meta", str) ||
+ !g_ascii_strcasecmp("M", str))
+ mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_META);
+ /* W = windows key, is linked to the Super_L/R buttons */
+ else if (!g_ascii_strcasecmp("Super", str) ||
+ !g_ascii_strcasecmp("W", str))
+ mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SUPER);
+ else if (!g_ascii_strcasecmp("Shift", str) ||
+ !g_ascii_strcasecmp("S", str))
+ mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT);
+ else if (!g_ascii_strcasecmp("Hyper", str) ||
+ !g_ascii_strcasecmp("H", str))
+ mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_HYPER);
+ else
+ g_message(_("Invalid modifier key \"%s\" in key/mouse binding"), str);
+
+ return mask;
+}
+
+gboolean translate_button(const gchar *str, guint *state, guint *button)
+{
+ gchar **parsed;
+ gchar *l;
+ gint i;
+ gboolean ret = FALSE;
+
+ parsed = g_strsplit(str, "-", -1);
+
+ /* first, find the button (last token) */
+ l = NULL;
+ for (i = 0; parsed[i] != NULL; ++i)
+ l = parsed[i];
+ if (l == NULL)
+ goto translation_fail;
+
+ /* figure out the mod mask */
+ *state = 0;
+ for (i = 0; parsed[i] != l; ++i) {
+ guint m = translate_modifier(parsed[i]);
+ if (!m) goto translation_fail;
+ *state |= m;
+ }
+
+ /* figure out the button */
+ if (!g_ascii_strcasecmp("Left", l)) *button = 1;
+ else if (!g_ascii_strcasecmp("Middle", l)) *button = 2;
+ else if (!g_ascii_strcasecmp("Right", l)) *button = 3;
+ else if (!g_ascii_strcasecmp("Up", l)) *button = 4;
+ else if (!g_ascii_strcasecmp("Down", l)) *button = 5;
+ else if (!g_ascii_strncasecmp("Button", l, 6)) *button = atoi(l+6);
+ if (!*button)
+ goto translation_fail;
+
+ ret = TRUE;
+
+translation_fail:
+ g_strfreev(parsed);
+ return ret;
+}
+
+gboolean translate_key(const gchar *str, guint *state, guint *keycode)
+{
+ gchar **parsed;
+ gchar *l;
+ gint i;
+ gboolean ret = FALSE;
+ KeySym sym;
+
+ parsed = g_strsplit(str, "-", -1);
+
+ *state = *keycode = 0;
+
+ /* first, find the key (last token) */
+ l = NULL;
+ for (i = 0; parsed[i] != NULL; ++i)
+ l = parsed[i];
+ if (l == NULL)
+ goto translation_fail;
+
+ /* figure out the mod mask */
+ *state = 0;
+ for (i = 0; parsed[i] != l; ++i) {
+ guint m = translate_modifier(parsed[i]);
+ if (!m) goto translation_fail;
+ *state |= m;
+ }
+
+ if (!g_ascii_strncasecmp("0x", l, 2)) {
+ gchar *end;
+
+ /* take it directly */
+ *keycode = strtol(l, &end, 16);
+ if (*l == '\0' || *end != '\0') {
+ g_message(_("Invalid key code \"%s\" in key binding"), l);
+ goto translation_fail;
+ }
+ } else {
+ /* figure out the keycode */
+ sym = XStringToKeysym(l);
+ if (sym == NoSymbol) {
+ g_message(_("Invalid key name \"%s\" in key binding"), l);
+ goto translation_fail;
+ }
+ *keycode = XKeysymToKeycode(obt_display, sym);
+ }
+ if (!*keycode) {
+ g_message(_("Requested key \"%s\" does not exist on the display"), l);
+ goto translation_fail;
+ }
+
+ ret = TRUE;
+
+translation_fail:
+ g_strfreev(parsed);
+ return ret;
+}
diff --git a/openbox/translate.h b/openbox/translate.h
new file mode 100644
index 0000000..bffdc9b
--- /dev/null
+++ b/openbox/translate.h
@@ -0,0 +1,29 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ translate.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef ob__translate_h
+#define ob__translate_h
+
+#include <glib.h>
+
+gboolean translate_button(const gchar *str, guint *state, guint *keycode);
+gboolean translate_key(const gchar *str, guint *state, guint *keycode);
+
+void RrImageSetDestroyFunc(RrImage *image, RrImageDestroyFunc func);
+
+#endif
diff --git a/openbox/window.c b/openbox/window.c
new file mode 100644
index 0000000..51806f9
--- /dev/null
+++ b/openbox/window.c
@@ -0,0 +1,229 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ window.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "window.h"
+#include "menuframe.h"
+#include "config.h"
+#include "dock.h"
+#include "client.h"
+#include "frame.h"
+#include "openbox.h"
+#include "prompt.h"
+#include "debug.h"
+#include "grab.h"
+#include "obt/prop.h"
+#include "obt/xqueue.h"
+
+static GHashTable *window_map;
+
+static guint window_hash(Window *w) { return *w; }
+static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; }
+
+void window_startup(gboolean reconfig)
+{
+ if (reconfig) return;
+
+ window_map = g_hash_table_new((GHashFunc)window_hash,
+ (GEqualFunc)window_comp);
+}
+
+void window_shutdown(gboolean reconfig)
+{
+ if (reconfig) return;
+
+ g_hash_table_destroy(window_map);
+}
+
+Window window_top(ObWindow *self)
+{
+ switch (self->type) {
+ case OB_WINDOW_CLASS_MENUFRAME:
+ return WINDOW_AS_MENUFRAME(self)->window;
+ case OB_WINDOW_CLASS_DOCK:
+ return WINDOW_AS_DOCK(self)->frame;
+ case OB_WINDOW_CLASS_CLIENT:
+ return WINDOW_AS_CLIENT(self)->frame->window;
+ case OB_WINDOW_CLASS_INTERNAL:
+ return WINDOW_AS_INTERNAL(self)->window;
+ case OB_WINDOW_CLASS_PROMPT:
+ return WINDOW_AS_PROMPT(self)->super.window;
+ }
+ g_assert_not_reached();
+ return None;
+}
+
+ObStackingLayer window_layer(ObWindow *self)
+{
+ switch (self->type) {
+ case OB_WINDOW_CLASS_DOCK:
+ return config_dock_layer;
+ case OB_WINDOW_CLASS_CLIENT:
+ return ((ObClient*)self)->layer;
+ case OB_WINDOW_CLASS_MENUFRAME:
+ case OB_WINDOW_CLASS_INTERNAL:
+ return OB_STACKING_LAYER_INTERNAL;
+ case OB_WINDOW_CLASS_PROMPT:
+ /* not used directly for stacking, prompts are managed as clients */
+ g_assert_not_reached();
+ break;
+ }
+ g_assert_not_reached();
+ return None;
+}
+
+ObWindow* window_find(Window xwin)
+{
+ return g_hash_table_lookup(window_map, &xwin);
+}
+
+void window_add(Window *xwin, ObWindow *win)
+{
+ g_assert(xwin != NULL);
+ g_assert(win != NULL);
+ g_hash_table_insert(window_map, xwin, win);
+}
+
+void window_remove(Window xwin)
+{
+ g_assert(xwin != None);
+ g_hash_table_remove(window_map, &xwin);
+}
+
+void window_manage_all(void)
+{
+ guint i, j, nchild;
+ Window w, *children;
+ XWMHints *wmhints;
+ XWindowAttributes attrib;
+
+ if (!XQueryTree(obt_display, RootWindow(obt_display, ob_screen),
+ &w, &w, &children, &nchild)) {
+ ob_debug("XQueryTree failed in window_manage_all");
+ nchild = 0;
+ }
+
+ /* remove all icon windows from the list */
+ for (i = 0; i < nchild; i++) {
+ if (children[i] == None) continue;
+ wmhints = XGetWMHints(obt_display, children[i]);
+ if (wmhints) {
+ if ((wmhints->flags & IconWindowHint) &&
+ (wmhints->icon_window != children[i]))
+ for (j = 0; j < nchild; j++)
+ if (children[j] == wmhints->icon_window) {
+ /* XXX watch the window though */
+ children[j] = None;
+ break;
+ }
+ XFree(wmhints);
+ }
+ }
+
+ for (i = 0; i < nchild; ++i) {
+ if (children[i] == None) continue;
+ if (window_find(children[i])) continue; /* skip our own windows */
+ if (XGetWindowAttributes(obt_display, children[i], &attrib)) {
+ if (attrib.map_state == IsUnmapped)
+ ;
+ else
+ window_manage(children[i]);
+ }
+ }
+
+ if (children) XFree(children);
+}
+
+static gboolean check_unmap(XEvent *e, gpointer data)
+{
+ const Window win = *(Window*)data;
+ return ((e->type == DestroyNotify && e->xdestroywindow.window == win) ||
+ (e->type == UnmapNotify && e->xunmap.window == win));
+}
+
+void window_manage(Window win)
+{
+ XWindowAttributes attrib;
+ gboolean no_manage = FALSE;
+ gboolean is_dockapp = FALSE;
+ Window icon_win = None;
+
+ grab_server(TRUE);
+
+ /* check if it has already been unmapped by the time we started
+ mapping. the grab does a sync so we don't have to here */
+ if (xqueue_exists_local(check_unmap, &win)) {
+ ob_debug("Trying to manage unmapped window. Aborting that.");
+ no_manage = TRUE;
+ }
+ else if (!XGetWindowAttributes(obt_display, win, &attrib))
+ no_manage = TRUE;
+ else {
+ XWMHints *wmhints;
+
+ /* is the window a docking app */
+ is_dockapp = FALSE;
+ if ((wmhints = XGetWMHints(obt_display, win))) {
+ if ((wmhints->flags & StateHint) &&
+ wmhints->initial_state == WithdrawnState)
+ {
+ if (wmhints->flags & IconWindowHint)
+ icon_win = wmhints->icon_window;
+ is_dockapp = TRUE;
+ }
+ XFree(wmhints);
+ }
+ /* This is a new method to declare that a window is a dockapp, being
+ implemented by Windowmaker, to alleviate pain in writing GTK+
+ dock apps.
+ http://thread.gmane.org/gmane.comp.window-managers.openbox/4881
+ */
+ if (!is_dockapp) {
+ gchar **ss;
+ if (OBT_PROP_GETSS_TYPE(win, WM_CLASS, STRING_NO_CC, &ss))
+ {
+ if (ss[0] && ss[1] && strcmp(ss[1], "DockApp") == 0)
+ is_dockapp = TRUE;
+ g_strfreev(ss);
+ }
+ }
+ }
+
+ if (!no_manage) {
+ if (attrib.override_redirect) {
+ ob_debug("not managing override redirect window 0x%x", win);
+ grab_server(FALSE);
+ }
+ else if (is_dockapp) {
+ if (!icon_win)
+ icon_win = win;
+ dock_manage(icon_win, win);
+ }
+ else
+ client_manage(win, NULL);
+ }
+ else {
+ grab_server(FALSE);
+ ob_debug("FAILED to manage window 0x%x", win);
+ }
+}
+
+void window_unmanage_all(void)
+{
+ dock_unmanage_all();
+ client_unmanage_all();
+}
diff --git a/openbox/window.h b/openbox/window.h
new file mode 100644
index 0000000..24a7d07
--- /dev/null
+++ b/openbox/window.h
@@ -0,0 +1,93 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ window.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __window_h
+#define __window_h
+
+#include "stacking.h"
+
+#include <X11/Xlib.h>
+#include <glib.h>
+
+typedef struct _ObWindow ObWindow;
+typedef struct _ObInternalWindow ObInternalWindow;
+
+typedef enum {
+ OB_WINDOW_CLASS_MENUFRAME,
+ OB_WINDOW_CLASS_DOCK,
+ OB_WINDOW_CLASS_CLIENT,
+ OB_WINDOW_CLASS_INTERNAL,
+ OB_WINDOW_CLASS_PROMPT
+} ObWindowClass;
+
+/* In order to be an ObWindow, you need to make this struct the top of your
+ struct */
+struct _ObWindow {
+ ObWindowClass type;
+};
+
+#define WINDOW_IS_MENUFRAME(win) \
+ (((ObWindow*)win)->type == OB_WINDOW_CLASS_MENUFRAME)
+#define WINDOW_IS_DOCK(win) \
+ (((ObWindow*)win)->type == OB_WINDOW_CLASS_DOCK)
+#define WINDOW_IS_CLIENT(win) \
+ (((ObWindow*)win)->type == OB_WINDOW_CLASS_CLIENT)
+#define WINDOW_IS_INTERNAL(win) \
+ (((ObWindow*)win)->type == OB_WINDOW_CLASS_INTERNAL)
+#define WINDOW_IS_PROMPT(win) \
+ (((ObWindow*)win)->type == OB_WINDOW_CLASS_PROMPT)
+
+struct _ObMenu;
+struct _ObDock;
+struct _ObDockApp;
+struct _ObClient;
+struct _ObPrompt;
+
+#define WINDOW_AS_MENUFRAME(win) ((struct _ObMenuFrame*)win)
+#define WINDOW_AS_DOCK(win) ((struct _ObDock*)win)
+#define WINDOW_AS_CLIENT(win) ((struct _ObClient*)win)
+#define WINDOW_AS_INTERNAL(win) ((struct _ObInternalWindow*)win)
+#define WINDOW_AS_PROMPT(win) ((struct _ObPrompt*)win)
+
+#define MENUFRAME_AS_WINDOW(menu) ((ObWindow*)menu)
+#define DOCK_AS_WINDOW(dock) ((ObWindow*)dock)
+#define CLIENT_AS_WINDOW(client) ((ObWindow*)client)
+#define INTERNAL_AS_WINDOW(intern) ((ObWindow*)intern)
+#define PROMPT_AS_WINDOW(prompt) ((ObWindow*)prompt)
+
+void window_startup (gboolean reconfig);
+void window_shutdown(gboolean reconfig);
+
+Window window_top (ObWindow *self);
+ObStackingLayer window_layer(ObWindow *self);
+
+ObWindow* window_find (Window xwin);
+void window_add (Window *xwin, ObWindow *win);
+void window_remove(Window xwin);
+
+/* Internal openbox-owned windows like the alt-tab popup */
+struct _ObInternalWindow {
+ ObWindowClass type;
+ Window window;
+};
+
+void window_manage_all(void);
+void window_manage(Window win);
+void window_unmanage_all(void);
+
+#endif
diff --git a/packaging/openbox.changes b/packaging/openbox.changes
new file mode 100644
index 0000000..8bcfb86
--- /dev/null
+++ b/packaging/openbox.changes
@@ -0,0 +1,7 @@
+* Tue Sep 04 2012 Geoffroy Van Cutsem <geoffroy.vancutsem@intel.com> release-3.5.0@171e290
+- Added session management dependency (was missing)
+- Added post and uninstall scripts to edit uxlaunch
+
+* Sat Sep 01 2012 Geoffroy Van Cutsem <geoffroy.vancutsem@intel.com> release-3.5.0@a903d1c
+- Initial submission to Tizen git/gerrit/OBS public infrastructure
+
diff --git a/packaging/openbox.spec b/packaging/openbox.spec
new file mode 100644
index 0000000..f1b6d20
--- /dev/null
+++ b/packaging/openbox.spec
@@ -0,0 +1,145 @@
+Name: openbox
+Summary: A highly configurable and standards-compliant X11 window manager
+Version: 3.5.0
+Release: 1
+Group: System/X11
+License: GPLv2+
+URL: http://openbox.org
+Source0: %{name}-%{version}.tar.bz2
+Requires: %{name}-libs = %{version}-%{release}
+BuildRequires: pkgconfig(libxml-2.0)
+BuildRequires: pkgconfig(xcursor)
+BuildRequires: pkgconfig(xrandr)
+BuildRequires: pkgconfig(xinerama)
+BuildRequires: pkgconfig(xext)
+BuildRequires: pkgconfig(xau)
+BuildRequires: pkgconfig(pango)
+BuildRequires: pkgconfig(sm)
+BuildRequires: gettext-tools
+BuildRequires: eglibc-devel
+BuildRequires: desktop-file-utils
+
+
+%description
+Openbox is a window manager designed explicity for standards-compliance and
+speed. It is fast, lightweight, and heavily configurable (using XML for its
+configuration data). It has many features that make it unique among window
+managers: window resistance, chainable key bindings, customizable mouse
+actions, multi-head/Xinerama support, and dynamically generated "pipe menus."
+
+For a full list of the FreeDesktop.org standards with which it is compliant,
+please see the COMPLIANCE file in the included documentation of this package.
+For a graphical configuration editor, you'll need to install the obconf
+package. For a graphical menu editor, you'll need to install the obmenu
+package.
+
+
+
+%package libs
+Summary: Shared libraries for %{name}
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description libs
+The %{name}-libs package contains shared libraries used by %{name}.
+
+%package devel
+Summary: Development files for %{name}
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+Requires: %{name}-libs = %{version}-%{release}
+Requires: pkgconfig
+Requires: pango-devel
+Requires: libxml2-devel
+Requires: glib2-devel
+
+%description devel
+The %{name}-devel package contains libraries and header files for
+developing applications that use %{name}.
+
+
+
+%prep
+%setup -q -n %{name}-%{version}
+
+%build
+/bin/sh bootstrap
+%configure --disable-static
+make %{?jobs:-j%jobs}
+
+%install
+rm -rf %{buildroot}
+%make_install
+
+# >> install post
+rm -rf %{buildroot}%{_datadir}/doc/%{name}
+#remove autostart stuff because uxlaunch allready starts stuff
+rm %{buildroot}/%{_sysconfdir}/xdg/%{name}/autostart
+# Remove the gnome-session stuff
+rm -rf %{buildroot}%{_datadir}/gnome-session
+# << install post
+desktop-file-install --delete-original \
+ --dir %{buildroot}%{_datadir}/applications \
+ %{buildroot}%{_datadir}/applications/*.desktop
+%find_lang %{name}
+
+%post libs
+/sbin/ldconfig
+# Set up uxlaunch to start openbox automatically
+if [ -e /etc/sysconfig/uxlaunch ]; then
+ #Comment out current session
+ sed -i 's/session/#session/' /etc/sysconfig/uxlaunch
+ # Add new session line
+ echo 'session=/usr/bin/openbox-session' >> /etc/sysconfig/uxlaunch
+fi
+
+%postun libs
+/sbin/ldconfig
+if [ -e /etc/sysconfig/uxlaunch ]; then
+ #Delete the openbox-session line
+ sed -i '/openbox-session/d' /etc/sysconfig/uxlaunch
+ # Restore previous session to whatever it was
+ sed -i 's/#session/session/' /etc/sysconfig/uxlaunch
+fi
+
+%files -f %{name}.lang
+%defattr(-,root,root,-)
+%doc AUTHORS CHANGELOG COMPLIANCE COPYING README
+%doc data/*.xsd data/menu.xml doc/rc-mouse-focus.xml
+%dir %{_sysconfdir}/xdg/%{name}/
+%config(noreplace) %{_sysconfdir}/xdg/%{name}/*
+%{_bindir}/gnome-panel-control
+%{_bindir}/gdm-control
+%{_bindir}/obxprop
+%{_bindir}/openbox*
+%{_libexecdir}/openbox-autostart
+%{_libexecdir}/openbox-xdg-autostart
+%{_datadir}/applications/%{name}.desktop
+%{_datadir}/gnome/wm-properties/%{name}.desktop
+%{_datadir}/themes/*
+%{_datadir}/pixmaps/%{name}.png
+%{_datadir}/xsessions/%{name}*.desktop
+%{_mandir}/man1/%{name}*.1*
+%{_mandir}/man1/obxprop.1*
+# >> files
+# << files
+
+
+%files libs
+%defattr(-,root,root,-)
+%{_libdir}/libobrender.so.*
+%{_libdir}/libobt.so.*
+# >> files libs
+# << files libs
+
+%files devel
+%defattr(-,root,root,-)
+%{_includedir}/%{name}
+%{_libdir}/libobrender.so
+%{_libdir}/libobt.so
+%{_libdir}/pkgconfig/*.pc
+# >> files devel
+# << files devel
+
diff --git a/po/LINGUAS b/po/LINGUAS
new file mode 100644
index 0000000..0db7a85
--- /dev/null
+++ b/po/LINGUAS
@@ -0,0 +1,32 @@
+sr sr@latin
+zh_TW
+zh_CN
+de
+es
+eu
+ca
+sv
+sk
+no
+fr
+ru
+pl
+pt
+pt_BR
+fi
+en@quot en@boldquot
+et
+cs
+nl
+ar
+bn_IN
+it
+vi
+ja
+uk
+hu
+lt
+lv
+tr
+da
+hr
diff --git a/po/Makevars b/po/Makevars
new file mode 100644
index 0000000..05231b8
--- /dev/null
+++ b/po/Makevars
@@ -0,0 +1,41 @@
+# Makefile variables for PO directory in any package using GNU gettext.
+
+# Usually the message domain is the same as the package name.
+DOMAIN = $(PACKAGE)
+
+# These two variables depend on the location of this directory.
+subdir = po
+top_builddir = ..
+
+# These options get passed to xgettext.
+XGETTEXT_OPTIONS = --keyword=_ --keyword=N_
+
+# This is the copyright holder that gets inserted into the header of the
+# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding
+# package. (Note that the msgstr strings, extracted from the package's
+# sources, belong to the copyright holder of the package.) Translators are
+# expected to transfer the copyright for their translations to this person
+# or entity, or to disclaim their copyright. The empty string stands for
+# the public domain; in this case the translators are expected to disclaim
+# their copyright.
+COPYRIGHT_HOLDER = Dana Jansens
+
+# This is the email address or URL to which the translators shall report
+# bugs in the untranslated strings:
+# - Strings which are not entire sentences, see the maintainer guidelines
+# in the GNU gettext documentation, section 'Preparing Strings'.
+# - Strings which use unclear terms or require additional context to be
+# understood.
+# - Strings which make invalid assumptions about notation of date, time or
+# money.
+# - Pluralisation problems.
+# - Incorrect English spelling.
+# - Incorrect formatting.
+# It can be your email address, or a mailing list address where translators
+# can write to without being subscribed, or the URL of a web page through
+# which the translators can contact you.
+MSGID_BUGS_ADDRESS = http://bugzilla.icculus.org
+
+# This is the list of locale categories, beyond LC_MESSAGES, for which the
+# message catalogs shall be used. It is usually empty.
+EXTRA_LOCALE_CATEGORIES =
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644
index 0000000..4509714
--- /dev/null
+++ b/po/POTFILES.in
@@ -0,0 +1,18 @@
+# List of source files containing translatable strings.
+openbox/actions.c
+openbox/actions/execute.c
+openbox/actions/exit.c
+openbox/client.c
+openbox/client_list_combined_menu.c
+openbox/client_list_menu.c
+openbox/client_menu.c
+openbox/config.c
+openbox/debug.c
+openbox/keyboard.c
+openbox/menu.c
+openbox/mouse.c
+openbox/openbox.c
+openbox/screen.c
+openbox/startupnotify.c
+openbox/translate.c
+openbox/prompt.c
diff --git a/po/ar.po b/po/ar.po
new file mode 100644
index 0000000..a9701b6
--- /dev/null
+++ b/po/ar.po
@@ -0,0 +1,476 @@
+# translation of openbox.pot to Arabic
+# Copyright (C) 2007 Dana Jansens
+# This file is distributed under the same license as the Openbox package.
+#
+# Khaled Hosny <khaledhosny@eglug.org>, 2007.
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.3\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2007-07-21 14:43+0300\n"
+"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
+"Language-Team: Arabic <doc@arabeyes.org>\n"
+"Language: ar\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms: nplurals=4; plural=n==1 ? 0 : n==2 ? 1 : n>=3 && n<=10 ? 2 : "
+"3\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr ""
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr ""
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr ""
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr ""
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Ùشلت ÙÙŠ تحويل المسار \"%s\" من utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr ""
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr ""
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr ""
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr ""
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr ""
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr ""
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr ""
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr ""
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr ""
+
+#: openbox/client.c:3539
+#, 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 ""
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr ""
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr ""
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "اذهب هناك..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "أدÙر أسطح المكتب"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "أضÙ٠سطح مكتب جديد (_A)"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "احذ٠آخر سطح مكتب (_R)"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "نواÙØ°"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "أسطح مكتب"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "كل أسطح المكتب"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "طبقة (_L)"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "دائما على السطح (_T)"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "طبيعي (_N)"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "دائما ÙÙŠ القاع (_B)"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "أرسÙÙ„ إلى سطح المكتب (_S)"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "قائمة العميل"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "استعÙد (_E)"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "انقل (_M)"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "حجّÙÙ… (_Z)"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "صغّر (_N)"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "كبّر (_X)"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "Ù„Ù٠لأعلى/لأسÙÙ„ (_R)"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "ضع/أزل الحوا٠(_D)"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "أغلق (_C)"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "سياق غير صحيح \"%s\" ÙÙŠ ارتباط الÙأرة"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "زر غير صحيح \"%s\" محدد ÙÙŠ مل٠الإعدادات"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "لم أستطÙع إنشاء الدليل '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "أغلق"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "يتعارض مع ارتباط المÙاتيح ÙÙŠ مل٠الإعدادات"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "لم أعثر على مل٠قائمة سليم \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Ùشل تنÙيذ أمر Ù„ pipe-menu \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "خرج غير سليم من pipe-menu \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "حاولت الوصول إلى القائمة \"%s\" لكنها غير موجودة"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "المزيد..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "زر غير صحيح \"%s\" ÙÙŠ ارتباط الÙأرة"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "لم أستطع تغيير المجلد المنزلي \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "تعذّر Ùتح العرض من متغير البيئة DISPLAY."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "تعذّر بدأ مكتبة obrender."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "خادم إكس لا يدعم المحليّة."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "لم أستطÙع ضبط Ù…ÙغيّÙرات المحليّة لخادم إكس."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr "لم أعثر على مل٠إعدادات سليم، سأستخدم بعض الإÙتراضيات البسيطة"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "لم أستطÙع تحميل سÙمة."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr ""
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Ùشلت إعادة التشغيل ÙÙŠ تنÙيذ Ù…ÙÙ†Ùّذ جديد \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "حقوق النسخ"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "الصيغة: openbox [options]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"الخيارات:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help اعرض هذه المساعدة ثم اخرج\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version اعرض النسخة ثم اخرج\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace استبدل مدير النواÙØ° الذي يعمل حاليا\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable عطّÙÙ„ الإتصال بمدير الجلسة\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"تمرير رسائل لمرّة تعمل من أوبن‌بوكس:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure أعÙد تحميل إعدادات أوبن‌بوكس\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart أعÙد تشغيل أوبن‌بوكس\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr ""
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"خيارات التنقيح:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync شغّل ÙÙŠ النمط المزامن\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug اعرض خرْج التنقيح\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus اعرض خرج التنقيح للتعامل مع البؤرة\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama شق العرض إلى شاشات xinerama زائÙØ©\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"من Ùضلك أبلغ عن العلل إلى %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr ""
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "معامل سطر أوامر غير سليم \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "يعمل مدير نواÙØ° بالÙعل على الشاشة %Id"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "تعذّر الحصول على انتقاء مدير النواÙØ° على الشاشة %Id"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "مدير النواÙØ° على الشاشة %Id لا وجود له"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "سطح المكتب %Ii"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "تشغيل %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Ù…Ùتاح Ù…ÙغيّÙر \"%s\" غير سليم ÙÙŠ ارتبط الÙأرة/لوحة المÙاتيح"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "رمز Ù…Ùتاح \"%s\" غير سليم ÙÙŠ ارتباط المÙتاح"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "اسم Ù…Ùتاح \"%s\" غير سليم ÙÙŠ ارتباط المÙتاح"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "المÙتاح المطلوب \"%s\" لا وجود له ÙÙŠ العرض"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr ""
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "لم أستطÙع Ø­Ùظ الجلسة إلى \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "خطأ أثناء Ø­Ùظ الجلسة إلى \"%s\": %s"
+
+#~ msgid "X Error: %s"
+#~ msgstr "خطأ إكس: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Ùشلت ÙÙŠ تنÙيذ \"%s\": %s"
diff --git a/po/bn_IN.po b/po/bn_IN.po
new file mode 100644
index 0000000..b6a7d0a
--- /dev/null
+++ b/po/bn_IN.po
@@ -0,0 +1,480 @@
+# translation of openbox to Bengali (India)
+# Copyright (C) 2007 Dana Jansens
+# This file is distributed under the same license as the Openbox package.
+#
+# Runa Bhattacharjee <runabh@gmail.com>, 2007.
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.2\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2007-06-01 19:02+0530\n"
+"Last-Translator: Runa Bhattacharjee <runabh@gmail.com>\n"
+"Language-Team: Bengali (India) <en@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 1.11.4\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr ""
+"অবৈধ করà§à¦® \"%s\"-র অনà§à¦°à§‹à¦§ জানানো হয়েছে। à¦à¦‡ ধরনের কোনো করà§à¦® বরà§à¦¤à¦®à¦¾à¦¨à§‡ উপসà§à¦¥à¦¿à¦¤ নেই।"
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr ""
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr ""
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr ""
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "\"%s\" পাথটি utf8 থেকে রূপানà§à¦¤à¦° করতে বà§à¦¯à¦°à§à¦¥"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr ""
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr ""
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr ""
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr ""
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr ""
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr ""
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr ""
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr ""
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr ""
+
+#: openbox/client.c:3539
+#, 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 ""
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr ""
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr ""
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "চিহà§à¦¨à¦¿à¦¤ সà§à¦¥à¦¾à¦¨à§‡ চলà§à¦¨..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr ""
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr ""
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr ""
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "উইনà§à¦¡à§‹"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "ডেসà§à¦•à¦Ÿà¦ª"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "সরà§à¦¬à¦ªà§à¦°à¦•à¦¾à¦° ডেসà§à¦•à¦Ÿà¦ª"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "সà§à¦¤à¦° (_L)"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "সরà§à¦¬à¦¦à¦¾ উপরে (_t)"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "সà§à¦¬à¦¾à¦­à¦¾à¦¬à¦¿à¦• (_N)"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "সরà§à¦¬à¦¦à¦¾ নীচে (_b)"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "ডেসà§à¦•à¦Ÿà¦ªà§‡ পাঠানো হবে (_S)"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "কà§à¦²à¦¾à§Ÿà§‡à¦¨à§à¦Ÿ মেনà§"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "পà§à¦¨à¦°à§à¦¦à§à¦§à¦¾à¦° (_e)"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "সà§à¦¥à¦¾à¦¨à¦¾à¦¨à§à¦¤à¦°à¦£ (_M)"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "মাপ পরিবরà§à¦¤à¦¨ (_z)"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "আইকন রূপে পà§à¦°à¦¦à¦°à§à¦¶à¦¨ (_n)"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "বড় করà§à¦¨ (_x)"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "উপরে/নীচে গà§à¦Ÿà¦¿à§Ÿà§‡ নিন (_R)"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "বিনà§à¦¯à¦¾à¦¸ পরিবরà§à¦¤à¦¨ (_D)"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "বনà§à¦§ করà§à¦¨ (_C)"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "মাউস বাইনà§à¦¡à¦¿à¦‚ সংকà§à¦°à¦¾à¦¨à§à¦¤ অবৈধ কনটেকà§à¦¸à¦Ÿ \"%s\""
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "কনফিগ ফাইলে অবৈধ বাটন \"%s\" উলà§à¦²à¦¿à¦–িত হয়েছে"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "'%s' ডিরেকà§à¦Ÿà¦°à¦¿ নিরà§à¦®à¦¾à¦£ করতে বà§à¦¯à¦°à§à¦¥: %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "বনà§à¦§ করà§à¦¨"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "কনফিগ ফাইলে কি-বাইনà§à¦¡à¦¿à¦‚ সংকà§à¦°à¦¾à¦¨à§à¦¤ দà§à¦¬à¦¨à§à¦¦à§à¦¬"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "বৈধ মেনৠফাইল \"%s\" সনাকà§à¦¤ করতে বà§à¦¯à¦°à§à¦¥"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "পাইপ-মেনৠ\"%s\"-র জনà§à¦¯ কমানà§à¦¡ সঞà§à¦šà¦¾à¦²à¦¨ করতে বà§à¦¯à¦°à§à¦¥: %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "পাইপ-মেনৠ\"%s\" থেকে অবৈধ ফলাফল পà§à¦°à¦¾à¦ªà§à¦¤ হয়েছে"
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "অনà§à¦ªà¦¸à§à¦¥à¦¿à¦¤ মেনৠ\"%s\" বà§à¦¯à¦¬à¦¹à¦¾à¦°à§‡à¦° পà§à¦°à¦šà§‡à¦·à§à¦Ÿà¦¾ হয়েছে"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "অতিরিকà§à¦¤..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "মাউস বাইনà§à¦¡à¦¿à¦‚ সংকà§à¦°à¦¾à¦¨à§à¦¤ অবৈধ বাটন \"%s\""
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত ডিরেকà§à¦Ÿà¦°à¦¿ \"%s\"-তে পরিবরà§à¦¤à¦¨ করতে বà§à¦¯à¦°à§à¦¥: %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "DISPLAY à¦à¦¨à¦­à¦¾à¦¶à¦°à¦¨à¦®à§‡à¦¨à§à¦Ÿ ভেরিয়েবলের মান পà§à¦°à§Ÿà§‹à¦— করে পà§à¦°à¦¦à¦°à§à¦¶à¦¨ আরমà§à¦­ করতে বà§à¦¯à¦°à§à¦¥à¥¤"
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "obrender লাইবà§à¦°à§‡à¦°à¦¿ আরমà§à¦­ করতে বà§à¦¯à¦°à§à¦¥à¥¤"
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X সারà§à¦­à¦¾à¦° দà§à¦¬à¦¾à¦°à¦¾ লোকেইল সমরà§à¦¥à¦¿à¦¤à¦¤ হয় না।"
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "X সারà§à¦­à¦¾à¦°à§‡à¦° জনà§à¦¯ লোকেইল মডিফায়ার নিরà§à¦§à¦¾à¦°à¦£ করতে বà§à¦¯à¦°à§à¦¥à¥¤"
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr "বৈধ কনফিগ ফাইল সনাকà§à¦¤ করতে বà§à¦¯à¦°à§à¦¥, কয়েকটি সাধারণ ডিফলà§à¦Ÿ মান পà§à¦°à§Ÿà§‹à¦— করা হবে।"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "থিম লোড করতে বà§à¦¯à¦°à§à¦¥à¥¤"
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr ""
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "পà§à¦¨à¦°à¦¾à¦®à§à¦­à§‡à¦° পরে নতà§à¦¨ à¦à¦•à§à¦¸à§‡à¦•à¦¿à¦‰à¦Ÿà§‡à¦¬à¦² \"%s\" সঞà§à¦šà¦¾à¦²à¦¨ করতে বà§à¦¯à¦°à§à¦¥: %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "সà§à¦¬à¦¤à§à¦¬à¦¾à¦§à¦¿à¦•à¦¾à¦° (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦ªà§à¦°à¦£à¦¾à¦²à§€: openbox [বিকলà§à¦ª]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"বিবিধ বিকলà§à¦ª:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help সহায়তা বারà§à¦¤à¦¾ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করে পà§à¦°à¦¸à§à¦¥à¦¾à¦¨\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version সংসà§à¦•à¦°à¦£ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করে পà§à¦°à¦¸à§à¦¥à¦¾à¦¨\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr ""
+" --replace বরà§à¦¤à¦®à¦¾à¦¨à§‡ চলমান উইনà§à¦¡à§‹ পরিচালন বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾ পরিবরà§à¦¤à¦¨ করা হবে\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr ""
+" --sm-disable সেশান পরিচালন বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾à¦° সাথে সংযোগ নিষà§à¦•à§à¦°à¦¿à§Ÿ করা হবে\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"চলমান Openbox ইনসà§à¦Ÿà§à¦¯à¦¾à¦¨à§à¦¸à§‡ বারà§à¦¤à¦¾ পà§à¦°à§‡à¦°à¦£:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Openbox-র কনফিগারেশন পà§à¦¨à¦°à¦¾à§Ÿ লোড করে\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Openbox পà§à¦¨à¦°à¦¾à¦°à¦®à§à¦­\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr ""
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"ডিবাগ করার বিভিনà§à¦¨ বিকলà§à¦ª:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync সিঙà§à¦•à§à¦°à§‹à¦¨à¦¾à¦¸ মোডে সঞà§à¦šà¦¾à¦²à¦¿à¦¤ হবে\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug ডিবাগ-à¦à¦° ফলাফল পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করে\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus ফোকাস হà§à¦¯à¦¾à¦¨à§à¦¡à¦²à¦¿à¦‚ সংকà§à¦°à¦¾à¦¨à§à¦¤ ডিবাগের ফলাফল পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করে\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama পà§à¦°à¦¦à¦°à§à¦¶à¦¨ কà§à¦·à§‡à¦¤à§à¦°à¦Ÿà¦¿ নকল xinerama পরà§à¦¦à¦¾à§Ÿ ভাগ করা হবে\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"অনà§à¦—à§à¦°à¦¹ করে %s-ঠবাগ সংকà§à¦°à¦¾à¦¨à§à¦¤ সূচনা দায়ের করà§à¦¨\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr ""
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "অবৈধ কমানà§à¦¡-লাইন আরà§à¦—à§à¦®à§‡à¦¨à§à¦Ÿ \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "à¦à¦•à¦Ÿà¦¿ উইনà§à¦¡à§‹ পরিচালন বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾ বরà§à¦¤à¦®à¦¾à¦¨à§‡ %d-ঠচলছে"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "পরà§à¦¦à¦¾ %d-ঠউইনà§à¦¡à§‹ পরিচালন বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾à¦° নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ অংশ পà§à¦°à¦¾à¦ªà§à¦¤ করতে বà§à¦¯à¦°à§à¦¥"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "পরà§à¦¦à¦¾ %d-র উপর চলমান উইনà§à¦¡à§‹ পরিচালন বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾à¦Ÿà¦¿ বনà§à¦§ করতে বà§à¦¯à¦°à§à¦¥"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+msgstr[1] ""
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "desktop %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "%s সঞà§à¦šà¦¾à¦²à¦¿à¦¤ হচà§à¦›à§‡"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "কি/মাউস বাইনà§à¦¡à¦¿à¦‚-র মধà§à¦¯à§‡ অবৈধ মডিফায়ার-কি \"%s\""
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "কি-বাইনà§à¦¡à¦¿à¦‚-র মধà§à¦¯à§‡ অবৈধ কি-কোড \"%s\""
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "কি-বাইনà§à¦¡à¦¿à¦‚-র মধà§à¦¯à§‡ অবৈধ কি-র নাম \"%s\""
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "অনà§à¦°à§‹à¦§ করা কি \"%s\", পà§à¦°à¦¦à¦°à§à¦¶à¦¨ কà§à¦·à§‡à¦¤à§à¦°à§‡ উপসà§à¦¥à¦¿à¦¤ নেই"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr ""
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "\"%s\"-র সেশান সংরকà§à¦·à¦£ করতে বà§à¦¯à¦°à§à¦¥: %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "\"%s\"-ঠসেশান সংরকà§à¦·à¦£à¦•à¦¾à¦²à§‡ সমসà§à¦¯à¦¾: %s"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X সংকà§à¦°à¦¾à¦¨à§à¦¤ তà§à¦°à§à¦Ÿà¦¿: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "\"%s\" সঞà§à¦šà¦¾à¦²à¦¨ করতে বà§à¦¯à¦°à§à¦¥: %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "\"%s\" করà§à¦®à§‡à¦° অবৈধ বà§à¦¯à¦¬à¦¹à¦¾à¦°à¥¤ করà§à¦® উপেকà§à¦·à¦¾ করা হবে।"
diff --git a/po/ca.po b/po/ca.po
new file mode 100644
index 0000000..b892ebc
--- /dev/null
+++ b/po/ca.po
@@ -0,0 +1,523 @@
+# Missatges en català per a openbox.
+# Copyright (C) 2007 Mikael Magnusson
+# This file is distributed under the same license as the openbox package.
+# David Majà Martínez <davidmaja@gmail.com>, 2007, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7.2\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2008-05-25 19:23+0200\n"
+"Last-Translator: David Majà Martínez <davidmaja@gmail.com>\n"
+"Language-Team: catalan\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "L'acció sollicitada \"%s\" no és vàlida. Aquesta acció no existeix."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "No"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Sí"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Executa"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "No s'ha pogut convertir el camí \"%s\" des de utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Cancel·la"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Surt"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Esteu segur de voler sortir?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Surt"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Esteu segur de voler sortir de Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Surt de Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Finestra sense nom"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "S'està finalitzant..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "No està responent"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Sembla que la finestra \"%s\" no està responent. Voleu forçar-la a "
+"finalitzar enviant el senyal %s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Finalitza el procés"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"Sembla que la finestra \"%s\" no està responent. Voleu desconnectar-la del "
+"servidor d'X?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Desconnecta"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Vés aquí..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Gestiona els escriptoris"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Afegeix un nou escriptori"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Suprimeix l'últim escriptori"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Finestres"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Escriptoris"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Tots els escriptoris"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Capa"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Sempre a so_bre"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normal"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Sempre a so_ta"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "A l'_escriptori"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Menú del client"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "Restaur_a"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Mou"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Redimen_siona"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Mi_nimitza"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximitza"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "En/Desen_rotlla"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Sense/Amb _decoració"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Tanca"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "El context \"%s\" no és vàlid en la vinculació del ratolí"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "El botó especificat al fitxer de configuració \"%s\" no és vàlid."
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "No és pot crear el directori '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Tanca"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Conflicte amb la tecla vinculada en el fitxer de configuració"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "No s'ha pogut trobar un fitxer de menú \"%s\" vàlid"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr ""
+"S'ha produït un error en executar l'ordre per al menú de conducte \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "La sortida del menú de conducte \"%s\" no és vàlida"
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "S'ha intentat accedir al menú \"%s\" ja que no existeix"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Més..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "El botó \"%s\" no és vàlid en la vinculació del ratolí"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "No s'ha pogut canviar al directori de l'usuari \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "No s'ha pogut obrir la pantalla des de la variable d'entorn DISPLAY"
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "S'ha produït un error en inicialitza la llibreria obrender."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "El servidor X no te suport per a idiomes"
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "No s'ha pogut assignar els modificadors del locale per al servidor X."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"No s'ha pogut trobat un fitxer de configuració vàlid, s'utilitzaran alguns "
+"valors predeterminats"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "No s'ha pogut carregar el tema."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"S'han trobat un o mes errors de sintaxi XML en analitzar el fitxer de "
+"configuració de Openbox. Per a més informació visualitza el stdout. L'últim "
+"error trobat estava al fitxer \"%s\" línia %d, amb el missatge: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Error de sintaxi de Openbox"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr ""
+"S'ha produït un error en tornar a iniciar i executar el nou executable \"%s"
+"\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Sintaxis: openbox [opcions]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Opcions:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Visualitza aquesta ajuda i surt\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Visualitza la versió i surt\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr ""
+" --replace Reemplaça el gestor de finestres que s'està executant "
+"actualment\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file FITXER\n"
+" Especifica el camí del fitxer de configuració a "
+"utilitzar\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Inhabilita la connexió amb el gestor de sessió\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"S'està transferint missatges a la instància del Openbox que s'està "
+"executant:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Torna a carregar la configuració de Openbox\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Torna a iniciar Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Surt de Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Opcions de depuració:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Executa en mode sincronitzat\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Mostra la sortida de depuració\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Mostra la sortida de depuració per a la gestió del "
+"focus\n"
+
+#: openbox/openbox.c:547
+#, fuzzy
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+" --debug-session Mostra la sortida de depuració per a la gestió del "
+"session management\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr ""
+" --debug-xinerama Divideix la visualització en pantalles xinerama "
+"falses\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Informeu dels errors a %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s necessita un argument\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Opció \"%s\" no vàlida a la línia d'ordres\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Encara s'està executant un gestor de finestres a la pantalla %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr ""
+"No s'ha pogut adquirir la selecció del gestor de finestres en la pantalla %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "El gestor de finestres de la pantalla %d no està sortint"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"El Openbox està configurat per a %d escriptori, però la sessió actual en te "
+"%d. S'està modificant la configuració del Openbox."
+msgstr[1] ""
+"El Openbox està configurat per a %d escriptoris, però la sessió actual en te "
+"%d. S'està modificant la configuració del Openbox."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "escriptori %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Executant %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr ""
+"La tecla modificadora \"%s\" no és vàlida en la vinculació de tecles/ratolí"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "El codi de tecla \"%s\" no és vàlid en la vinculació de tecles"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "El nom de la tecla \"%s\" no és vàlid en la vinculació de tecles"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "La tecla seleccionada \"%s\" no existeix a la pantalla"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "D'acord"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Surt de Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file necessita un argument\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "L'acció SessionLogout no està disponible ja que el Openbox s'ha compilat "
+#~ "sense suport per a la gestió de sessions"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "No s'ha pogut desar la sessió a \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "S'ha produït un error mentre es desava la sessió a \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "No esteu connectats al gestor de sessions"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Error d'X: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "No s'ha pogut executar \"%s\": %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "L'ús de l'acció \"%s\" no és vàlid. S'ignorarà aquesta acció."
diff --git a/po/cs.po b/po/cs.po
new file mode 100644
index 0000000..79d84cf
--- /dev/null
+++ b/po/cs.po
@@ -0,0 +1,501 @@
+# Czech translation for Openbox.
+# Copyright (C) 2007 Dana Jansens
+# This file is distributed under the same license as the Openbox 3 package.
+# tezlo <tezlo@gmx.net>, 2007
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7\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-17 17:00+0100\n"
+"Last-Translator: tezlo <tezlo@gmx.net>\n"
+"Language-Team: Czech <cs@li.org>\n"
+"Language: cs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Požadována neplatná akce \"%s\". Žádná taková akce neexistuje."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Ne"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Ano"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Spustit"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Nepodařilo se převést cestu \"%s\" z utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Zrušit"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Konec"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "UrÄitÄ› odhlásit?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Odhlásit"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "UrÄitÄ› chcete ukonÄit Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "UkonÄit Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Nepojmenované Okno"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "UkonÄuji..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Neodpovídá"
+
+#: openbox/client.c:3539
+#, 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 "Okno \"%s\" nedpovídá. Chcete jej ukonÄit signálem %s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "UkonÄit Proces"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr "Okno \"%s\" neodpovídá. Chcete jej odpojit od X serveru?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Odpojit"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Jdi tam..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Spravovat plochy"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Přidat novou plochu"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Odstranit poslední plochu"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Okna"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Plochy"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "VÅ¡echny plochy"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "Vrs_tva"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Vždy na_vrchu"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normální"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Vždy ve_spodu"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "_Poslat na plochu"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Menu klienta"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "_Obnovit"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "Přes_unout"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Veli_kost"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Mi_nimalizovat"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximalizovat"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "S_rolovat/Vyrolovat"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Oz_dobit/Odzdobit"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Zavřít"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Neplatný kontext \"%s\" v nastavení myši"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Neplatné tlaÄítko \"%s\" v konfiguraÄním souboru"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Nepodařilo se vytvořit adresář '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Zavřít"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Konflikt klávesových zkratek v konfiguraÄním souboru"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Nepodařilo se najít platný menu soubor \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Nepodařilo se spustit příkaz pro pipe-menu \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Neplatný výstup z pipe-menu \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Pokus o přístup k menu \"%s\", ale ono neexistuje"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Víc..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Neplatné tlaÄítko \"%s\" v nastavení myÅ¡i"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Nepodařilo se přejít do domácího adresáře \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Nepodařilo se otevřít displej z proměnné prostředí DISPLAY."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Nepodařilo se inicializovat knihovnu obrender."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X server nepodporuje lokalizaci."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Nelze nastavit modifikátory lokalizace pro X server."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"NepodaÅ™ilo se najít platný konfiguraÄní soubor, pokraÄuji s výchozím "
+"nastavením"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "NepodaÅ™ilo se naÄíst motiv."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"PÅ™i naÄítání konfiguraÄních suborů nalezena jedna nebo více syntaktických "
+"chyb, více informací na standartním výstupu. Poslední zaznamenaná chyba je v "
+"souboru \"%s\" na řádku %d, se zprávou: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox Chyba Syntaxe"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Při restartu se nepodařilo spustit nový program \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Syntaxe: openbox [pÅ™epínaÄe]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"PÅ™epínaÄe:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Zobrazit tuto nápovÄ›du a skonÄit\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Zobrazit verzi a skonÄit\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Nahradit běžící window manager\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file FILE Cesta ke konfiguraÄnímu souboru\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Nepřipojovat se k session manageru\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Zasílání zpráv běžící instanci Openbox:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Znovu naÄíst konfiguraci Openbox\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Restartovat Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit UkonÄit Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Ladící pÅ™epínaÄe:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Spustit v synchronním módu\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Zobrazit ladící výstup\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus Zobrazit ladící výstup pro správu oken\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Rozdělit displej na falešné obrazovky xinerama\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Prosím hlašte chyby na %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s vyžaduje argument\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Neplatný argument příkazové řádky \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Na obrazovce %d již nějaký window manager běží"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "NepodaÅ™ilo se získat výseÄ pro window manager na obrazovce %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Window manager na obrazovce %d ne a ne skonÄit"
+
+# TODO the rest of the file has plochy for plural and plochu for singular
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, fuzzy, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox je nakonfigurován pro %d ploch, ale souÄasná session má %d. "
+"Konfigurace Openboxu bude změněna."
+msgstr[1] ""
+"Openbox je nakonfigurován pro %d ploch, ale souÄasná session má %d. "
+"Konfigurace Openboxu bude změněna."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "plochu %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Spouštím %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Neplatný modifikátor \"%s\" v nastavení klávesnice/myši"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Neplatný kód klávesy \"%s\" v nastevení"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Neplatné jméno klávesy \"%s\" v nastavení"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Požadovaná klávesa \"%s\" na displeji neexistuje"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "UkonÄit Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file vyžaduje argument\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "Akce SessionLogout není k dispozici jelikož byl Openbox zkompilován bez "
+#~ "podpory session manageru"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Nepodařilo se uložit session do \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Chyba během ukládání session do \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Nepřipojen k session manageru"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X Chyba: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Nepodařilo se spustit \"%s\": %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "Neplatné užití akce \"%s\". Akce bude ignorována."
diff --git a/po/da.po b/po/da.po
new file mode 100644
index 0000000..9b5bc40
--- /dev/null
+++ b/po/da.po
@@ -0,0 +1,505 @@
+# Danish messages for openbox
+# Copyright (C) 2007 Dana Jansens
+# This file is distributed under the same license as the openbox package.
+#
+# Jesper Sander <sander.contrib@gmail.com>, 2008
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7.2\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2008-08-19 16:50+0100\n"
+"Last-Translator: Jesper Sander <sander.contrib@gmail.com>\n"
+"Language-Team: None\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Ugyldig operation \"%s\" anmodet. Operationen findes ikke."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Nej"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Ja"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Udfør"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Fejl ved konvertering af stien \"%s\" fra utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Afbryd"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Afslut"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Er du sikker på at du vil logge ud?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Log Ud"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Er du sikker på at du vil afslutte Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Afslut Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Unavngivet vindue"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Dræber..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Svarer Ikke"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Vinduet \"%s\" svarer ikke. Vil du udføre tvunget afslutning ved at sende %s "
+"signalet?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Afslut proces"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr "Vinduet \"%s\" svarer ikke. Vil du frakoble vinduet fra X-serveren?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Frakoble"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "GÃ¥ der..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "HÃ¥ndter skrivebord"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Nyt skrivebord"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Fjern sidste skrivebord"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Vinduer"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Skrivebord"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Alle skriveborde"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "La_g"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Altid ø_verst"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "Nor_mal"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Altid _nederst"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "_Send til skrivebord"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Klient-menu"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "G_endan"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Flyt"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Ændre s_tørrelse"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "_Minimer"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximer"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "_Rul op/ned"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Fjern/tilføj _dekoration"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Luk"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Ugyldig indhold \"%s\" i muse-kombination"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Ugyldig tast \"%s\" specificeret i konfigurationsfilen"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Kan ikke oprette mappe '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Luk"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Konflikt med taste-kombinationer i konfigurationsfilen"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Kan ikke finde en gyldig menufil \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Kunne ikke udføre kommando for pipe-menu \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Ugyldig uddata fra pipe-menuen \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Forsøgte at åbne menuen \"%s\", men denne findes ikke"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Mere..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Ugyldig knap \"%s\" i muse-kombination"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Kan ikke skifte til hjemmekataloget \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Kunne ikke åbne displayet fra DISPLAY-miljøvariablen"
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Kunne ikke starte obrender-biblioteket."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X-serveren understøtter ikke lokalisering."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Kan ikke indstille lokaliseringsmodifikatorene for X-serveren."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Kunne ikke finde en gyldig konfigurationsfil, bruger nogle simple "
+"standardværdier"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Kan ikke hente et tema."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"En eller flere XML-syntaksfejl blev fundet ved læsning af "
+"konfigurationsfilerne til Openbox. Se stdout for mere information. Den "
+"sidste fejl som blev set var i fil \"%s\", linie %d, med beskeden: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox syntaksfejl"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Kunne ikke starte nyt program ved genstart: \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Syntaks: openbox [argumenter]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Tilvalg:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Vis denne hjælpetekst og afslut\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Vis versionsnummeret og afslut\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Erstat den kørende vinduesbehandler\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file FIL Specificer stien til konfigurationsfilen du vil "
+"benytte\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Deaktiver forbindelsen til sessionsbehandleren\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Sender beskeder til en kørende Openbox-instans:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Opdater Openbox' konfiguration\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Genstart Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Afslut Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Fejlsøgningsmuligheder:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Kør i synkron-modus\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Vis fejlsøgningsinformation\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Vis fejlsøgningsinformation for fokus-håndtering\n"
+
+#: openbox/openbox.c:547
+#, fuzzy
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+" --debug-session Vis fejlsøgningsinformation for session-håndtering\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Split displayet for \"falske\" xinerama-skærme\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Rapporter venligst fejl til %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s kræver et argument\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Ugyldig kommandolinie-argument \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "En vindusbehandler kører allerede på skærm %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Kunne ikke hente vindusbehandlerens markering på skærm %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Vinduesbehandleren på skærm %d vil ikke afslutte"
+
+# TODO Figure out how to handle this case, the second number has the "desktop"
+# on it here, but gettext looks at the first number only. If we split it up in
+# two strings then we can't swap the order from the .po file...
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Aktiv session har %2$d skriveborde, mens Openbox er konfigureret til %1$d. "
+"Benytter indstillingerne for den aktive session."
+msgstr[1] ""
+"Aktiv session har %2$d skriveborde, mens Openbox er konfigureret til %1$d. "
+"Benytter indstillingerne for den aktive session."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "skrivebord %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Kører %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Ugyldig modifikationstast \"%s\" i kombination for tast/mus"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Ugyldig tastekode \"%s\" i tastekombination"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Ugyldig tastenavn \"%s\" i tastekombination"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Ønsket tast \"%s\" eksisterer ikke i displayet"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Afslut Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file kræver et argument\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "SessionLogout er ikke tilgænglig, fordi Openbox blev kompileret uden "
+#~ "understøttelse for sessionsbehandling"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Kan ikke gemme sessionen til \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Fejl mens session blev gemt til \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Ikke forbundet til en sessionsbehandler"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Fejl i X: %s"
diff --git a/po/de.po b/po/de.po
new file mode 100644
index 0000000..8d12a1f
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,517 @@
+# German messages for openbox.
+# Copyright (C) 2007 Mikael Magnusson
+# This file is distributed under the same license as the openbox package.
+# Sebastian Vahl <svahl@web.de>, 2006.
+# Simon A. Wilper <simonaw@openoffice.org>, Apr 2007
+# Peter Schwindt <schwindt@ba-loerrach.de>
+# Finn Zirngibl <finn@s23.org>, 2008
+# Florian Walch <florian.walch@gmx.at>, 2008
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7\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"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\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."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Nein"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Ja"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Ausführen"
+
+#: 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"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Abbrechen"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Beenden"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Sind Sie sicher, dass Sie sich abmelden wollen?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Abmelden"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Sind Sie sicher, dass Openbox beendet werden soll?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Beende Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Unbenanntes Fenster"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Wird beendet..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Reagiert nicht"
+
+#: openbox/client.c:3539
+#, 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?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Beende Prozess"
+
+#: openbox/client.c:3545
+#, 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 "
+"werden?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Trennen"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Hierher wechseln..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Desktops verwalten"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Neuen Desktop hinzufügen"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Letzten Desktop entfernen"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Fenster"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Desktops"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Alle Desktops"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Layer"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Immer im _Vordergrund"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normal"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Immer im _Hintergrund"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "_An Desktop senden"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Client menu"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "Wi_ederherstellen"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "Vers_chieben"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "_Größe ändern"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Mi_nimieren"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximieren"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "Auf/Ab_rollen"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Dekoration entfernen/_Dekorieren"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Schließen"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Maus-Binding 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"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Das Verzeichnis '%s' konnte nicht angelegt werden: %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Schließen"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Konflikt mit Tastenkombination in der Konfigurationsdatei"
+
+#: 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"
+
+#: 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"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Ungültige Ausgabe vom pipe-menu \"%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"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Mehr..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Maus-Binding enthält ungültigen Button \"%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"
+
+#: 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."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Konnte die obrender-Bibliothek nicht initialisieren."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "Die gewählte Lokalisierung wird vom X-Server nicht unterstützt."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr ""
+"Die Lokalisierungsmodifizierer für den X-Server konnten nicht gesetzt werden."
+
+#: 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"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Konnte kein Thema laden."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"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"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox Syntax-Fehler"
+
+#: 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"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Syntax: openbox [Optionen]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Optionen:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Diese Hilfe anzeigen und beenden\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Version anzeigen und beenden\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Den aktuell laufenden Fenstermanager ersetzen\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file DATEI Pfad zur Konfigurationsdatei\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Keine Verbindung zum Sitzungsmanager aufbauen\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Nachrichten an eine laufende Openbox-Instanz weiterleiten:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Openbox's Konfiguration neu laden\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Openbox neu starten\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Beende Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Debugging Optionen:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync im Synchronisierungsmodus starten\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Debugging-Informationen anzeigen\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"
+
+#: 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 "
+"anzeigen\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr ""
+" --debug-xinerama Anzeige in künstliche Xinerama-Bildschirme aufteilen\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Bitte melden Sie Bugreports an: %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s erfordert einen Parameter\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Ungültiges Kommandozeilen Argument \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Ein Fenstermanager läuft bereits auf Bildschirm %d"
+
+#: 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"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Der Fenstermanager auf Bildschirm %d beendet sich nicht"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"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."
+msgstr[1] ""
+"Openbox wurde für %d Desktops konfiguriert, aber die aktuelle Sitzung hat "
+"%d. Ãœberschreibe die Openbox-Konfiguration."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "Desktop %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Starte %s"
+
+#: 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"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+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"
+
+#: 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"
+
+#: 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."
diff --git a/po/en@boldquot.po b/po/en@boldquot.po
new file mode 100644
index 0000000..0df4f96
--- /dev/null
+++ b/po/en@boldquot.po
@@ -0,0 +1,499 @@
+# English translations for openbox package.
+# Copyright (C) 2011 Dana Jansens
+# This file is distributed under the same license as the openbox package.
+# Automatically generated, 2011.
+#
+# All this catalog "translates" are quotation characters.
+# The msgids must be ASCII and therefore cannot contain real quotation
+# characters, only substitutes like grave accent (0x60), apostrophe (0x27)
+# and double quote (0x22). These substitutes look strange; see
+# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html
+#
+# This catalog translates grave accent (0x60) and apostrophe (0x27) to
+# left single quotation mark (U+2018) and right single quotation mark (U+2019).
+# It also translates pairs of apostrophe (0x27) to
+# left single quotation mark (U+2018) and right single quotation mark (U+2019)
+# and pairs of quotation mark (0x22) to
+# left double quotation mark (U+201C) and right double quotation mark (U+201D).
+#
+# When output to an UTF-8 terminal, the quotation characters appear perfectly.
+# When output to an ISO-8859-1 terminal, the single quotation marks are
+# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to
+# grave/acute accent (by libiconv), and the double quotation marks are
+# transliterated to 0x22.
+# When output to an ASCII terminal, the single quotation marks are
+# transliterated to apostrophes, and the double quotation marks are
+# transliterated to 0x22.
+#
+# This catalog furthermore displays the text between the quotation marks in
+# bold face, assuming the VT100/XTerm escape sequences.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openbox 3.5.0-rc1\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2011-08-01 18:20+0200\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"Language: en\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Invalid action “%s†requested. No such action exists."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "No"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Yes"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Execute"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Failed to convert the path “%s†from utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Cancel"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Exit"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Are you sure you want to log out?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Log Out"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Are you sure you want to exit Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Exit Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Unnamed Window"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Killing..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Not Responding"
+
+#: openbox/client.c:3539
+#, 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 ""
+"The window “%s†does not seem to be responding. Do you want to force "
+"it to exit by sending the %s signal?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "End Process"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"The window “%s†does not seem to be responding. Do you want to "
+"disconnect it from the X server?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Disconnect"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Go there..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Manage desktops"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Add new desktop"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Remove last desktop"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Windows"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Desktops"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "All desktops"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Layer"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Always on _top"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normal"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Always on _bottom"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "_Send to desktop"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Client menu"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "R_estore"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Move"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Resi_ze"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Ico_nify"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximize"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "_Roll up/down"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Un/_Decorate"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Close"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Invalid context “%s†in mouse binding"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Invalid button “%s†specified in config file"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Unable to make directory '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Close"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Conflict with key binding in config file"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Unable to find a valid menu file “%sâ€"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Failed to execute command for pipe-menu “%sâ€: %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Invalid output from pipe-menu “%sâ€"
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Attempted to access menu “%s†but it does not exist"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "More..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Invalid button “%s†in mouse binding"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Unable to change to home directory “%sâ€: %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Failed to open the display from the DISPLAY environment variable."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Failed to initialize the obrender library."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X server does not support locale."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Cannot set locale modifiers for the X server."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr "Unable to find a valid config file, using some simple defaults"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Unable to load a theme."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file “%s†line %d, with message: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox Syntax Error"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Restart failed to execute new executable “%sâ€: %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Syntax: openbox [options]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Options:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Display this help and exit\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Display the version and exit\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Replace the currently running window manager\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file FILE Specify the path to the config file to use\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Disable connection to the session manager\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Reload Openbox's configuration\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Restart Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Exit Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Debugging options:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Run in synchronous mode\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr " --startup CMD Run CMD after starting\n"
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Display debugging output\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus Display debugging output for focus handling\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+" --debug-session Display debugging output for session management\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Split the display into fake xinerama screens\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Please report bugs at %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s requires an argument\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Invalid command line argument “%sâ€\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "A window manager is already running on screen %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Could not acquire window manager selection on screen %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "The WM on screen %d is not exiting"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[1] ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "desktop %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Running %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Invalid modifier key “%s†in key/mouse binding"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Invalid key code “%s†in key binding"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Invalid key name “%s†in key binding"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Requested key “%s†does not exist on the display"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
diff --git a/po/en@quot.po b/po/en@quot.po
new file mode 100644
index 0000000..e9af2e4
--- /dev/null
+++ b/po/en@quot.po
@@ -0,0 +1,496 @@
+# English translations for openbox package.
+# Copyright (C) 2011 Dana Jansens
+# This file is distributed under the same license as the openbox package.
+# Automatically generated, 2011.
+#
+# All this catalog "translates" are quotation characters.
+# The msgids must be ASCII and therefore cannot contain real quotation
+# characters, only substitutes like grave accent (0x60), apostrophe (0x27)
+# and double quote (0x22). These substitutes look strange; see
+# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html
+#
+# This catalog translates grave accent (0x60) and apostrophe (0x27) to
+# left single quotation mark (U+2018) and right single quotation mark (U+2019).
+# It also translates pairs of apostrophe (0x27) to
+# left single quotation mark (U+2018) and right single quotation mark (U+2019)
+# and pairs of quotation mark (0x22) to
+# left double quotation mark (U+201C) and right double quotation mark (U+201D).
+#
+# When output to an UTF-8 terminal, the quotation characters appear perfectly.
+# When output to an ISO-8859-1 terminal, the single quotation marks are
+# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to
+# grave/acute accent (by libiconv), and the double quotation marks are
+# transliterated to 0x22.
+# When output to an ASCII terminal, the single quotation marks are
+# transliterated to apostrophes, and the double quotation marks are
+# transliterated to 0x22.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openbox 3.5.0-rc1\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2011-08-01 18:20+0200\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"Language: en\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Invalid action “%s†requested. No such action exists."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "No"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Yes"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Execute"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Failed to convert the path “%s†from utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Cancel"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Exit"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Are you sure you want to log out?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Log Out"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Are you sure you want to exit Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Exit Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Unnamed Window"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Killing..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Not Responding"
+
+#: openbox/client.c:3539
+#, 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 ""
+"The window “%s†does not seem to be responding. Do you want to force it to "
+"exit by sending the %s signal?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "End Process"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"The window “%s†does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Disconnect"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Go there..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Manage desktops"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Add new desktop"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Remove last desktop"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Windows"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Desktops"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "All desktops"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Layer"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Always on _top"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normal"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Always on _bottom"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "_Send to desktop"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Client menu"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "R_estore"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Move"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Resi_ze"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Ico_nify"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximize"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "_Roll up/down"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Un/_Decorate"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Close"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Invalid context “%s†in mouse binding"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Invalid button “%s†specified in config file"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Unable to make directory '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Close"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Conflict with key binding in config file"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Unable to find a valid menu file “%sâ€"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Failed to execute command for pipe-menu “%sâ€: %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Invalid output from pipe-menu “%sâ€"
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Attempted to access menu “%s†but it does not exist"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "More..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Invalid button “%s†in mouse binding"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Unable to change to home directory “%sâ€: %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Failed to open the display from the DISPLAY environment variable."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Failed to initialize the obrender library."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X server does not support locale."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Cannot set locale modifiers for the X server."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr "Unable to find a valid config file, using some simple defaults"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Unable to load a theme."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file “%s†line %d, with message: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox Syntax Error"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Restart failed to execute new executable “%sâ€: %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Syntax: openbox [options]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Options:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Display this help and exit\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Display the version and exit\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Replace the currently running window manager\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file FILE Specify the path to the config file to use\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Disable connection to the session manager\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Reload Openbox's configuration\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Restart Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Exit Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Debugging options:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Run in synchronous mode\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr " --startup CMD Run CMD after starting\n"
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Display debugging output\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus Display debugging output for focus handling\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+" --debug-session Display debugging output for session management\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Split the display into fake xinerama screens\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Please report bugs at %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s requires an argument\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Invalid command line argument “%sâ€\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "A window manager is already running on screen %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Could not acquire window manager selection on screen %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "The WM on screen %d is not exiting"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[1] ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "desktop %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Running %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Invalid modifier key “%s†in key/mouse binding"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Invalid key code “%s†in key binding"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Invalid key name “%s†in key binding"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Requested key “%s†does not exist on the display"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
diff --git a/po/es.po b/po/es.po
new file mode 100644
index 0000000..44c5e51
--- /dev/null
+++ b/po/es.po
@@ -0,0 +1,523 @@
+# Spanish messages for openbox.
+# Copyright (C) 2005 Mikael Magnusson
+# This file is distributed under the same license as the openbox package.
+# Miguel Calleja Gómez <mcg79@lycos.es>, 2005.
+# Gustavo Varela <gustavo.varela [en] gmail [punto] com>, 2007
+# David Merino <rastiazul at yahoo . com>, 2007.
+# Elián Hanisch <lambdae2@gmail.com>, 2008.
+# Nicolás de la Torre <ndelatorre@gmail.com>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7.2\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2008-05-04 16:39-0300\n"
+"Last-Translator: Nicolás de la Torre <ndelatorre@gmail.com>\n"
+"Language-Team: español <es@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "La acción \"%s\" solicitada es inválida. No existe tal acción."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "No"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Sí"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Ejecutar"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Falló al convertir la ruta \"%s\" desde utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Salir"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "¿Está seguro que desea salir?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Salir"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "¿Está seguro que desea salir de Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Salir de Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Ventana sin nombre"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Terminando..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "No está respondiendo"
+
+#: openbox/client.c:3539
+#, 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 ""
+"La ventana \"%s\" no parece estar respondiendo. ¿Desea forzarla a salir "
+"enviándole la señal %s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Finalizar proceso"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"La ventana \"%s\" no parece estar respondiendo. ¿Desea desconectarla del "
+"servidor X?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Desconectar"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Ir ahí..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Administrar escritorios"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Añadir un nuevo escritorio"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Remover el último escritorio"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Ventanas"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Escritorios"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Todos los escritorios"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Capa"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Siempre _encima"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normal"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Siempre _debajo"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "_Enviar al escritorio"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Menú del cliente"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "Rest_aurar"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Mover"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Redimen_sionar"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Mi_nimizar"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximizar"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "En/Desen_rollar"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "_Decorar"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Cerrar"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Contexto inválido \"%s\" en mouse binding"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Botón inválido \"%s\" especificado en el archivo de configuración"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "No se puede crear el directorio '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Cerrar"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Conflicto con la combinación de teclas en el archivo de configuración"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "No es posible encontrar un archivo de menú \"%s\" válido"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Falló al ejecutar el comando para el pipe-menu \"%s\": \"%s\""
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Salida inválida del pipe-menu \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Intentó acceder al menú \"%s\" pero este no existe"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Más..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Botón inválido \"%s\" en mouse binding"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "No es posible cambiar al directorio home \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Falló abrir la pantalla desde la variable de entorno DISPLAY"
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Falló la inicialización de la librería obrender"
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "El servidor X no soporta localizaciones."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr ""
+"No se puede establecer los modificadores de localización para el servidor X."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"No es posible encontrar un archivo de configuración válido, usando algunos "
+"valores por defecto"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "No es posible cargar el tema."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Uno o más errores de sintaxis XML fueron encontrados leyendo los archivos de "
+"configuración de Openbox. Ver salida (stdout) para mas información. El "
+"último error viste estaba en el archivo \"%s\" linea %d, con el mensaje: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox Error de Sintaxis"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "El reinicio falló en ejecutar el nuevo ejecutable \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Sintaxis: openbox [opciones]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Opciones:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Muestra esta ayuda y sale\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Muestra la versión y sale\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr ""
+" --replace Remplaza el gestor de ventanas que esta corriendo "
+"actualmente\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file ARCHIVO\n"
+" Especifique la ruta del archivo de configuración a "
+"usar\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr ""
+" --sm-disable Deshabilita la conexión con el gestor de sesión\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Pasando mensajes a la instancia que esta corriendo de Openbox:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Recarga la configuración de Openbox\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Reinicia Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Cierra Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Opciones de depuración:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Correr en modo sincrónico\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Mostrar salida del depurador\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Mostrar salida del depurador para el manejo del foco\n"
+
+#: openbox/openbox.c:547
+#, fuzzy
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+" --debug-session Mostrar salida del depurador para el manejo del "
+"session\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr ""
+" --debug-xinerama Separar la visualización en pantallas de xinerama "
+"falsas\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Por favor reportar errores a %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s requiere un argumento\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Argumento de la línea de comando inválido \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Un gestor de ventanas ya esta corriendo en la pantalla %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "No se pudo obtener la selección del gestor de ventanas en pantalla %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "El WM en la pantalla %d no está saliendo"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox está configurado para escritorio %d, pero la sesión actual a %d. "
+"Invalidando la configuración de Openbox."
+msgstr[1] ""
+"Openbox está configurado para escritorios %d, pero la sesión actual a %d. "
+"Invalidando la configuración de Openbox."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "Escritorio %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Ejecutando %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr ""
+"Modificador de tecla \"%s\" inválido en combinaciones de teclas o ratón"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Código de tecla \"%s\" inválido en combinaciones de teclas"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Nombre de tecla \"%s\" inválido en combinaciones de teclas"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Tecla solicitada \"%s\" no existe en la pantalla"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Salir de Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file requiere un argumento\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "La acción SessionLogout no esta disponible ya que Openbox fue construido "
+#~ "sin soporte de manejo de sesiones"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "No se puede salvar la sesión a \"%s\": \"%s\""
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Error mientras se salvaba la sesión a \"%s\": \"%s\""
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Sin conexión a un manejador de sesiones"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Error en X: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Falló al ejecutar \"%s\": %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "Uso inválido de la acción \"%s\". La acción sera ignorada."
diff --git a/po/et.po b/po/et.po
new file mode 100644
index 0000000..01330c2
--- /dev/null
+++ b/po/et.po
@@ -0,0 +1,500 @@
+# Openboxi eesti keele tõlge
+# translation of openbox to Estonian
+# Copyright (C) 2007 Dana Jansens
+# This file is distributed under the same license as the Openbox 3 package.
+#
+# Andres Järv <andresjarv@gmail.com>, 2007.
+# Mihkel <turakas gmail com>, 2010
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.11.1\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2010-04-21 21:40+0300\n"
+"Last-Translator: mihkel <turakas@gmail.com>\n"
+"Language-Team: Estonian <et@li.org>\n"
+"Language: et\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Taotleti kehtetut käsklust \"%s\". Sellist käsklust pole olemas."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Ei"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Jah"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Käivita"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Raja \"%s\" ümberkodeerimine UTF8-st ebaõnnestus"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Katkesta"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Välju"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Kas oled kindel, et soovid välja logida?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Logi välja"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Kas oled kindel, et soovid OpenBoxist väljuda?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Välju Openbox-st"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Nimetu aken"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Tapan..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Ei vasta"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Paistab, et aken \"%s\" ei vasta enam. Kas soovid teda jõuga väljuma sundida "
+"saates %s signaali?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Lõpeta protsess"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"Paistab, et aken \"%s\" ei vasta enam. Kas soovid ta X serverist lahti "
+"ühendada?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Ãœhenda lahti"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Mine sinna..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Halda töölaudu"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Lisa uus töölaud"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Eemalda viimane töölaud"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Aknad"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Töölauad"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Kõik töölauad"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Kiht"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Aken teiste _peal"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normaalne"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Aken teiste _all"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "_Saada töölauale"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Kliendi menüü"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "_Taasta"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Liiguta"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Muuda _suurust"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Muuda _ikooniks"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ksimeeri"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "_Rulli üles/alla"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Äär_ed sisse/välja"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "S_ulge"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Vigane kontekst \"%s\" hiire kiirklahvides"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Vigane nupp \"%s\" määratud seadistuste failis"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Kausta '%s' tegemine ebaõnnestus: %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Sulge"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Konflikt kiirklahviga seadistuste failis"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Ei suudetud leida kehtivat menüüfaili \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Ei suudetud käivitada torumenüü \"%s\" käsku: %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Vigane väljund torumenüüst \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Üritati ligi pääseda menüüle \"%s\", aga seda pole olemas"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Rohkem..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Vigane nupp \"%s\" hiire kiirklahvides"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Ei suudetud siseneda kodukataloogi \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "DISPLAY keskkonnamuutujas oleva ekraani avamine ebaõnnestus."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Obrender-damise teegi käivitamine ebaõnnestus."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X server ei toeta lokaati."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Ei suudetud sättida lokaadimuutujaid X serveri jaoks."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Kehtiva seadistuste faili leidmine ebaõnnestus, kasutatakse lihtsaid "
+"vaikimisi seadeid"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Teema laadimine ebaõnnestus."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Üks või enam XML süntaki viga leiti Openboxi seadistuse faili parsides. "
+"Rohkem infot leiad stdout-st. Viimane viga oli failis \"%s\", real %d ja "
+"sõnum oli: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openboxi süntaksi viga"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Taaskäivitusel ebaõnnestus uue käivitusfaili \"%s\" käivitamine: %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Autoriõigused (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Süntaks: openbox [seaded]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Seaded:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Selle abi kuvamine ja väljumine\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Versiooni kuvamine ja väljumine\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Hetkel töötava aknahalduri asendamine\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file FAIL Määra kasutatava seadistuste faili teekond\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Seansihalduriga ühenduse keelamine\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Jooksvale Openboxi seansile sõnumite edastamine:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Openboxi konfiguratsioon uuesti laadimine\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Openboxi taaskäivitamine\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Välju Openbox-st\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Silumise seaded:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Sünkroonselt jooksutamine\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Silumisväljundi kuvamine\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus Fookusekäsitluse siluriväljundi kuvamine\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Ekraani võlts-Xinerama ekraanideks jagamine\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Palun teata vigadest siia %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s nõuab argumenti\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Vigane käsurea argument \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Ekraanil %d juba jookseb aknahaldur"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Ei suuda hankida aknahaldurite loetelu ekraanil %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Aknahaldur ekraanil %d ei sulgu"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox on seadistatud %d töölauale, aga aktiivsel seansil on %d. Tühistan "
+"Openboxi seadistuse."
+msgstr[1] ""
+"Openbox on seadistatud %d töölauale, aga aktiivsel seansil on %d. Tühistan "
+"Openboxi seadistuse."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "töölaud %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Jooksev %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Vigane muutujaklahv \"%s\" hiire/klaviatuuri kiirklahvides"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Vigane klahvikood \"%s\" kiirklahvil"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Vigane klahvinimi \"%s\" kiirklahvil"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Soovitud klahvi \"%s\" ei ole sellel ekraanil"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "Sobib"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Seansi \"%s\" salvestamine ebaõnnestus: %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Seansi \"%s\" salvestamisel ilmnes viga: %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Pole ühendatud seansihalduriga"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X-i viga: %s"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "SessionLogout tegevust pole saadaval, kuna Openbox on kompileeritud "
+#~ "seansi haldamise toeta"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "\"%s\" käivitamine ebaõnnestus: %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "Käskluse \"%s\" kasutus on kehtetu. Käsklust ignoreeritakse."
diff --git a/po/eu.po b/po/eu.po
new file mode 100644
index 0000000..01501e7
--- /dev/null
+++ b/po/eu.po
@@ -0,0 +1,506 @@
+# Basque translation for openbox.
+# Copyright (C) 2008 Inko Illarramendi Arancibia
+# This file is distributed under the same license as the openbox package.
+# Inko Illarramendi Arancibia <inkoia@gmail.com>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7.2\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2008-10-22 18:06+0100\n"
+"Last-Translator: Inko I. A. <inkoia@gmail.com>\n"
+"Language-Team: Inko I. A. <inkoia@gmail.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Eskatutako \"%s\" ekintza baliogabea. Ez da ekintza hori existitzen."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Ez"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Bai"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Exekutatu"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Hutsegitea \"%s\" helbidea utf8-tik bihurtzean"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Ezeztatu"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Irten"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Ziur al zaude saioa itxi nahi duzula?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Saioa Itxi"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Ziur al zaude Openbox-etik irten nahi duzula?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Openbox-etik Irten"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Izenik gabeko leihoa"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Akabatzen..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Erantzunik Ez"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Badirudi \"%s\" leihoak ez duela erantzuten. Nahi al duzu istea behartu %s "
+"seinalea bidaliz?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Prozesua Amaitu"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"Badirudi \"%s\" leihoak ez duela erantzuten. Nahi al duzu leihoa X "
+"zerbitzaritik deskonektatu?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Deskonektatu"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Hona joan..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Idazmahaiak kudeatu"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "Idazmahai berria _gehitu"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "Azken idazmahaia _ezabatu"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Leihoak"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Idazmahaiak"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Idazmahai guztiak"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Geruza"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Beti _gainean"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Ohikoa"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Beti _azpian"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "_Bidali idazmahaira"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Bezero menua"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "Berr_ezarri"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Mugitu"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "_Tamaina aldatu"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Iko_notu"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximizatu"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "Bildu/_Zabaldu"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Des/_Dekoratu"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Itxi"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Baliogabeko \"%s\" testuingurua sagu elkarketan"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Konfigurazio fitxategian zehaztutako \"%s\" botoia baliogabea"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Ezin da '%s' direktorioa sortu: %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Itxi"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Gatazka konfigurazio fitxategiko tekla elkarketarekin"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Ezin da \"%s\" baliozko menu fitxategi bat aurkitu"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Hutsegitea \"%s\" pipe-menuarentzat komandoa exekutatzean: %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Baliogabeko irteera \"%s\" pipe-menutik"
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "\"%s\" menua atzitzen saiatu da baina ez da existitzen"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Gehiago..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Baliogabeko \"%s\" botoia sagu elkarketan"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Ezin da \"%s\" hasiera direktoriora aldatu: %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Hutsegitea pantaila irekitzean DISPLAY ingurune aldagaitik."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Hutsegitea obrender liburutegia hasieratzean."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X zerbitzariak ez du locale euskarririk."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Ezin da locale modifikatzailerik ezarri X zerbitzariarentzat."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Ezin da baliozko konfigurazio fitxategirik aurkitu, hainbat aukera lehenetsi "
+"sinple erabiltzen"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Ezin da gai bat kargatu."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"XML sintaxi errore bat edo gehiago aurkitu dira Openbox konfigurazio "
+"fitxategiak interpretatzerakoan. Ikusi stdout informazio gehiago jasotzeko. "
+"Azken errorea \"%s\" fitxategian %d lerroan izan da, mezu honekin: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox sintaxi errorea"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Berrabiarazteak hutsegitea \"%s\" exekutagarri berria exekutatzean: %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Sintaxia: openbox [aukerak]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Aukerak:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Mezu hau erakutsi eta irten\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Bertsioa bistarazi eta irten\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr ""
+" --replace Ordezkatu exekutatzen ari den leiho-kudeatzailea\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+"--config-file FILE Zehaztu erabiltzeko konfigurazio fitxategirako bidea\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Ezgaitu saio kudeatzailearekiko konexioa\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Exekutatzen ari den Openbox instantzia bati mezuak pasatzen:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Birkargatu Openbox-en konfigurazioa\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Berrabiarazi Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Itxi Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Arazketa aukerak:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Modu sinkronoan exekutatu\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Arazketa irteera erakutsi\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus Erakutsi arazketa irteera foku maneiurako\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Zatitu pantaila xinerama pantaila faltsuetan\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"%s helbidean erroreen berri eman mesedez\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s argumentu bat behar du\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "\"%s\" komando lerro argumentu baliogabea\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr ""
+"Bistaratzeko %d pantailan aurretik leiho-kudeatzaile bat exekutatzen ari da"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr ""
+"Ezin izan da eskuratu leiho-kudeatzailearen hautapena bistaratzeko %d "
+"pantailan"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "%d bistaratze pantailako leiho-kudeatzailea ez da irteten"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, fuzzy, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox %d idazmahaientzat konfiguratua dago, baina uneko saioak %d dauzka. "
+"Openbox konfigurazioa gainjartzen."
+msgstr[1] ""
+"Openbox %d idazmahaientzat konfiguratua dago, baina uneko saioak %d dauzka. "
+"Openbox konfigurazioa gainjartzen."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "%i Idazmahaia"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Egikaritzen %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr " tekla/sagu elkarketan \"%s\" modifikatzaile tekla baliogabea"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr " tekla elkarketan \"%s\" tekla kode baliogabea"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr " tekla elkarketan \"%s\" tekla izen baliogabea"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Eskatutako \"%s\" tekla ez da pantaila existitzen"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "Ados"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Openbox-etik Irten"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file argumentu bat behar du\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "SessionLogout ekintza ez dago eskuragarri, Openbox saio kudetzaile "
+#~ "gaitasun gabe konpilatua izan baitzen"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Ezin da saioa \"%s\"-n gorde: %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Errorea saioa \"%s\"-n gordetzean: %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Saio kudeatzaile batera ez konektatua"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X errorea: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Hutsegitea \"%s\" exekutatzean: %s"
diff --git a/po/fi.po b/po/fi.po
new file mode 100644
index 0000000..7cc1e93
--- /dev/null
+++ b/po/fi.po
@@ -0,0 +1,499 @@
+# Openbox Finnish translation.
+# Copyright (C) 2007 Mikael Magnusson
+# This file is distributed under the same license as the openbox package.
+# Pauli Virtanen <pauli.virtanen@hut.fi>, 2005.
+# Lauri Hakko <aperculum@gmail.com>, 2008.
+# Elias Julkunen <elias.julkunen@gmail.com>, 2008.
+# Jarkko Piiroinen <jarkkop@iki.fi>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.11\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2010-03-13 21:56+0100\n"
+"Last-Translator: Lauri Hakko <aperculum@gmail.com>\n"
+"Language-Team: None\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Pyydettiin virheellinen toiminto \"%s\". Toimintoa ei ole olemassa."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Ei"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Kyllä"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Suorita"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Polun \"%s\" muuntaminen utf8:sta epäonnistui"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Peruuta"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Sulje"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Haluatko varmasti kirjautua ulos?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Kirjaudu ulos"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Haluatko varmasti sulkea Openboxin"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Sulje Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Nimetön ikkuna"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Tapetaan..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Ei vastaa"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Ikkuna \"%s\" ei näytä vastaavan. Haluatko sulkea sen lähettämällä sille "
+"singaalin %s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Lopeta prosessi"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"Ikkuna \"%s\" ei näytä vastaavan. Haluatko katkaista sen yhteyden X-"
+"palvelimeen?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Katkaise yhteys"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Näytä tämä..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Työtilojen hallinta"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Lisää uusi työtila"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Poista viimeisin työtila"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Ikkunat"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Työtilat"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Kaikkiin työtiloihin"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Kerros"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Aina _päällimmäisenä"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Tavallinen"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Aina _alimmaisena"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "_Lähetä työtilaan"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Ikkunan valikko"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "_Palauta"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "S_iirrä"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "_Muuta kokoa"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Pie_nennä"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Suurenn_a"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "Rullaa _ylös/alas"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "(Epä)_reunusta"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Sulje"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Virheellinen asiayhteys \"%s\" hiirisidonnoissa"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Asetustiedostossa määritelty painike \"%s\" on virheellinen"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Hakemiston '%s' luonti epäonnistui: %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Sulje"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Päällekäisiä näppäinsidontoja asetustiedostossa"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Toimivaa valikkotiedostoa ei löytynyt \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Putkivalikon suorittaminen epäonnistui \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Virheellinen tulos putkivalikosta \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Valikon \"%s\" lukemista yritettiin, mutta sitä ei ole olemassa"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Lisää..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Virheellinen painike \"%s\" hiirisidonnoissa"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Kotihakemistoon \"%s\" vaihtaminen epäonnistui: %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Näytön avaaminen DISPLAY-muuttujasta epäonnistui."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Obrender-kirjaston käynnistäminen epäonnistui."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X-palvelin ei tue maa-asetusta."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Maa-asetusmuuttujia ei voitu tehdä X-palvelimelle."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Kelvollista asetustiedostoa ei löytynyt, käytetään yksinkertaisia "
+"oletusarvoja"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Teeman lataaminen epäonnistui."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Yksi tai useampi XML-syntaksivirhe löytyi asetustiedostoa käsiteltäessä. Lue "
+"stdout saadaksesi lisätietoja. Viimeisin virhe oli tiedostossa \"%s\" "
+"rivillä %d: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox syntaksivirhe"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr ""
+"Uudelleenkäynnistys ei onnistunut käynnistämään uutta ohjelmaa \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Tekijänoikeudet (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Syntaksi: openbox [valitsin]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Käyttö:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Näytä tämä ohje ja poistu\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Näytä version tiedot ja poistu\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Korvaa käynnissä oleva ikkunointiohjelma\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file FILE Määritä käytettävän asetustiedoston polku\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Estä yhteys istuntojen hallintaan\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Komentojen antaminen käynnissä olevalle Openboxille:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Lataa Openboxin asetustiedosto uudelleen\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Käynnistä Openbox uudelleen\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Sulje Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Vianjäljityksen asetukset:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Aja synkronointi-tilassa\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Näytä vianjäljitystuloste\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus Näytä vianjäljitystuloste ikkunavalitsimelle\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Jaa näyttö kahteen vale-xinerama-ruutuun\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Ilmoita virheistä: %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s tarvitsee argumentin\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Virheellinen valitsin \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Ikkunointiohjelma on jo käynnissä näytöllä %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Ikkunointiohjelman valinta ruudulla %d ei onnistunut"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Ikkunointiohjelma ruudulla %d ei sulkeudu"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox on asetettu käyttämään %d työtilaa, mutta nykyisessä istunnossa "
+"työtiloja on %d. Ohitetaan Openboxin asetus."
+msgstr[1] ""
+"Openbox on asetettu käyttämään %d työtilaa, mutta nykyisessä istunnossa "
+"työtiloja on %d. Ohitetaan Openboxin asetus."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "työtila %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Suoritetaan %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Virheellinen valintanäppäin \"%s\" näppäin- tai hiirisidonnoissa"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Virheellinen näppäinkoodi \"%s\" pikanäppäimissä"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Virheellinen näppäin \"%s\" pikanäppäimissä"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Pyydettyä näppäintä \"%s\" ei ole olemassa näytöllä"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Istuntoa ei voitu tallentaa hakemistoon \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Virhe tallennettaessa istuntoa hakemistoon \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Ei yhteyttä istunnon hallintaan"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X-virhe: %s"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "SessionLogout tapahtuma ei ole suoritettavissa, koska Openbox käännettiin "
+#~ "ilman istunnon hallinnan tukea"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Ohjelman \"%s\" suorittaminen epäonnistui: %s"
diff --git a/po/fr.po b/po/fr.po
new file mode 100644
index 0000000..36a2908
--- /dev/null
+++ b/po/fr.po
@@ -0,0 +1,527 @@
+# French translation of Openbox.
+# Copyright (C) 2004 Mikael Magnusson
+# This file is distributed under the same license as the Openbox package.
+#
+# tioui <leonptitlouis@wanadoo.fr>, 2004.
+# Cyrille Bagard <nocbos@gmail.com>, 2007-2008.
+# Jacques BON <jbon@cafcom.net>, 2007.
+# Éric Lassauge <lassauge@users.sf.net>, 2008
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7\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-02 02:06+0100\n"
+"Last-Translator: Cyrille Bagard <nocbos@gmail.com>\n"
+"Language-Team: français <fr@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Action demandée invalide \"%s\". Une telle action n'existe pas."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Non"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Oui"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Exécuter"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Échec de la conversion du chemin « %s » depuis l'UTF-8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Annuler"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Quitter"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Etes vous certain de vouloir vous déconnecter ?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Déconnexion"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Etes vous certain de vouloir quitter Openbox ?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Quitter Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Fenêtre sans nom"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Tue..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Ne répond pas"
+
+#: openbox/client.c:3539
+#, 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 ""
+"La fenêtre \"%s\" semble ne pas répondre. Voulez vous la forcer à se "
+"terminer en envoyant un signal %s ?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Fin de processus"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"La fenêtre \"%s\" semble ne pas répondre. Voulez vous la déconnecter du "
+"serveur X ?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Déconnexion"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Aller là..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Gérer les bureaux"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Ajouter un bureau"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Supprimer le dernier bureau"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Fenêtres"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Bureaux"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Tous les bureaux"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Disposition"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "_Toujours au premier plan"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normal"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Toujours en _arrière plan"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "En_voyer vers le bureau"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Menu de la fenêtre"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "R_estaurer"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "Dé_placer"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Red_imensionner"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Ico_nifier"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximiser"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "En/Dé_rouler"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Ne pas/Dé_corer"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Fermer"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Contexte « %s » invalide dans le paramétrage de la souris"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Bouton « %s » indiqué dans le fichier de configuration invalide"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Impossible de créer le répertoire « %s » : %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Fermer"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Conflit entre les raccourcis clavier dans le fichier de configuration"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Impossible de trouver un fichier de menus valide « %s »"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Échec lors de l'exécution de la commande pour un pipe-menu « %s » : %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Sortie du pipe-menu invalide « %s »"
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Tentative d'accès au menu « %s » qui n'existe pas"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Plus..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Bouton « %s » invalide dans le paramétrage de la souris"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Impossible de changer vers le répertoire de l'utilisateur « %s » : %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr ""
+"Échec de l'ouverture de l'affichage depuis la variable d'environnement "
+"DISPLAY."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Échec de l'initialisation de la bibliothèque obrender."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "Le serveur X ne supporte pas la localisation."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr ""
+"Impossible d'appliquer les modifications de localisation pour le serveur X."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Impossible de trouver un fichier de configuration valide, utilisation de "
+"défauts simples"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Impossible de charger un thème."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Une ou plusieurs erreurs de syntaxe XML ont été trouvées lors de l'analyse "
+"des fichiers de configuration d'Openbox. Voir stdout pour plus "
+"d'information. La dernière erreur vue était dans le fichier \"%s\", ligne "
+"%d, avec le message : %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Erreur de syntaxe Openbox"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr ""
+"Le redémarrage n'a pas réussi à exécuter le nouvel exécutable « %s » : %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Syntaxe : openbox [options]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Options :\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Affiche cette aide et quitte\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Affiche la version et quitte\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr ""
+" --replace Remplace le gestionnaire de fenêtres actuellement en "
+"usage\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file FILE Spécifie le chemin du fichier de configuration à "
+"utiliser\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr ""
+" --sm-disable Désactive la connexion au gestionnaire de sessions\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Passage de messages à l'instance d'Openbox en cours :\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Recharge la configuration d'Openbox\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Redémarre Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Sortir d'Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Options de déboguage :\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Exécute en mode synchrone\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Affiche la sortie de déboguage\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Affiche la sortie de déboguage pour la gestion du "
+"focus\n"
+
+#: openbox/openbox.c:547
+#, fuzzy
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+" --debug-session Affiche la sortie de déboguage pour la gestion du "
+"session\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr ""
+" --debug-xinerama Découpe l'affichage en écrans xinerama factices\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Veuillez soumettre les rapports de bogues à %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s requiert un argument\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Argument de la ligne de commande invalide « %s »\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Un gestionnaire de fenêtres est déjà lancé sur l'écran %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr ""
+"Impossible d'acquérir la sélection du gestionnaire de fenêtres pour l'écran "
+"%d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr ""
+"Le gestionnaire de fenêtres sur l'écran %d n'est pas en train de se terminer"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox est configuré pour %d bureau, mais la session en a %d. Ceci "
+"supplante la configuration d'Openbox."
+msgstr[1] ""
+"Openbox est configuré pour %d bureaux, mais la session en a %d. Ceci "
+"supplante la configuration d'Openbox."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "bureau %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Exécution de %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr ""
+"Touche de modification « %s » invalide dans le paramétrage du clavier / de "
+"la souris"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Code de touche « %s » invalide dans le raccourci clavier"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Nom de touche « %s » invalide dans le raccourci clavier"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "La touche demandée « %s » n'existe pas pour l'affichage"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Quitter Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file requiert un argument\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "L'action SessionLogout n'est pas disponible comme Openbox a été construit "
+#~ "sans support de gestion de session"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Impossible de sauvegarder la session dans « %s » : %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Erreur lors de la sauvegarde de la session depuis « %s » : %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Non connecté à un gestionnaire de session"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Erreur X : %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Échec de l'exécution de « %s » : %s"
diff --git a/po/hr.po b/po/hr.po
new file mode 100644
index 0000000..dacabfb
--- /dev/null
+++ b/po/hr.po
@@ -0,0 +1,503 @@
+# Croatian messages for openbox.
+# Copyright (C) 2009 Mikael Magnusson
+# This file is distributed under the same license as the openbox package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7.2\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2009-04-05 16:53+0200\n"
+"Last-Translator: boljsa <asjlob AT vip.hr>\n"
+"Language-Team: <asjlob AT vip.hr>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Nevažeća akcija \"%s\" zatražena. Takva akcija ne postoji."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Ne"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Da"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Izvrši"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Neuspio pokušaj pretvorbe putanje \"%s\" iz utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Odustani"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Izađi"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Jeste li sigurni da se želite odjaviti?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Odjava"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Jeste li sigurni da želite zatvoriti Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Zatvori Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Neimenovan Prozor"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Ubijanje..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Ne Odgovara"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Prozor \"%s\" ne reagira. Želite li forsirati izlaženje šaljući %s signal?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Završetak Procesa"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"Prozor \"%s\" ne reagira. Želite li prekinuti njegovu vezu sa X serverom?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Prekid veze"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Idi tamo..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Upravljanje radnim površinama"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Dodaj novu radnu površinu"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Ukloni zadnju radnu površinu"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Prozori"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Radne Površine"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Sve radne površine"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Sloj"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Uvijek na _vrhu"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normalno"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Uvijek na _dnu"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "Pošalji na _radnu površinu"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Izbornik klijenta"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "O_bnovi"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Pomicanje"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Prom_jena veliÄine"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Mi_nimizacija"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "M_aksimizacija"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "_Okretanje gore/dolje"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Ne/_Dekoriranje"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Zatvori"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Nevažeći kontekst \"%s\" u povezivanju miša"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Nevažeće dugme \"%s\" specificirano u konfiguracijskoj datoteci"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Ne mogu stvoriti direktorij '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Zatvori"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Konflikt sa povezivanjem tipki u konfiguracijskoj datoteci"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Ne mogu pronaći važeću datoteku izbornika \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Neuspio pokušaj izvršavanja naredbe za cijev-izbornik \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Nevažeći izlaz za cijev-izbornik \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Pokušavam pristupiti izborniku \"%s\" ali on ne postoji"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Više..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Nevažeće dugme \"%s\" u povezivanju miša"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Ne mogu doći u home direktorij \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Neuspio pokušaj otvaranja zaslona iz DISPLAY varijable okruženja."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Neuspio pokušaj inicijalizacije obrender biblioteke."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X server ne podržava lokalno."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Ne mogu postaviti lokalne modifikatore za X server."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Ne mogu pronaći važeću konfiguracijsku datoteku, koriteći neke jednostavne "
+"standarde"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Ne mogu pokrenuti temu."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Jedna ili više XML pogreški u sintaksi su pronađene prilikom analize Openbox "
+"konfiguracijskih datoteka. Pogledajte stdout za više informacija. Zadnja "
+"pogreška je u datoteci \"%s\" u liniji %d, sa porukom: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox Pogreška u Sintaksi"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Restart je bio neusješan za izvršenje novog izvršnog \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Sintaksa: openbox [opcije]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Opcije:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Prikazuje ovu pomoć i izlazi\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Prikazuje verziju i izlazi\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr ""
+" --replace Zamjenjuje trenutno pokrenut upravitelj prozora\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file FILE Specificira putanju do konfiguracijske datoteke koja "
+"se koristi\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Onemogućuje vezu sa upraviteljom sesija\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Prosljeđuje poruke pokrenutoj Openbox instanci:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Osvježava Openbox konfiguraciju\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Restartira Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Izlazi iz Openbox-a\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Opcije traženja pogrešaka:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Pokretanje u sinkronizacijskom modu\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Prikazuje izlaz traženja pogrešaka\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Prikazuje izlaz traženja pogrešaka za rukovanje "
+"fokusom\n"
+
+#: openbox/openbox.c:547
+#, fuzzy
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+" --debug-session Prikazuje izlaz traženja pogrešaka za rukovanje "
+"sessionom\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Podijeli zaslon u lažne xinerama zaslone\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Molimo prijavite pogrešku na %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s zahtjeva argument\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Nevažeći argument komandne linije \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Upravitelj prozora je već pokrenut na zaslonu %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Ne mogu ostvariti odabir upravitelja prozora na zaslonu %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Upravitelj prozora na zaslonu %d ne izlazi"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, fuzzy, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox je konfiguriran za %d radnu površinu, ali trenutna sesija ima %d. "
+"Prepisujem preko Openbox konfiguracije."
+msgstr[1] ""
+"Openbox je konfiguriran za %d radnu površinu, ali trenutna sesija ima %d. "
+"Prepisujem preko Openbox konfiguracije."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "radna površina %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Pokrenuto %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Nevažeći modifikacijski kljuÄ \"%s\" u povezivanju tipke/miÅ¡"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Nevažeći kod kljuÄa \"%s\" u povezivanju tipki"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Nevažeće ime tipke \"%s\" u povezivanju tipki"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Traženi kljuÄ \"%s\" ne postoji na zaslonu"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Zatvori Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file zahtjeva argument\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "Akcija SessionLogout nije dostupna otkad je Openbox izgrađen bez podrške "
+#~ "upravljanja sesijama"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Ne mogu spremiti sesiju u \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Pogreška prilokom spremanja sesije u \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Nije povezan sa upraviteljem sesija"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X Pogreška: %s"
diff --git a/po/hu.po b/po/hu.po
new file mode 100644
index 0000000..52ba588
--- /dev/null
+++ b/po/hu.po
@@ -0,0 +1,496 @@
+# Hungarian translation of openbox.
+# Copyright (C) 2007 Dana Jansens
+# This file is distributed under the same license as the openbox package.
+# Robert Kuszinger <hiding@freemail.hu>, 2007.
+# Laszlo Dvornik <dvornik@gnome.hu>, 2010.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openbox 3.4.10\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2010-01-14 11:04+0100\n"
+"Last-Translator: Laszlo Dvornik <dvornik@gnome.hu>\n"
+"Language-Team: Hungarian <translation-team-hu@lists.sourceforge.net>\n"
+"Language: hu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Érvénytelen művelet \"%s\". Nem létezik ilyen művelet."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Nem"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Igen"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Végrehajtás"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Az útvonalat nem sikerült átalakítani utf8-ból: \"%s\""
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Mégsem"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Kilépés"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Biztos ki akar jelentkezni?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Kijelentkezés"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Biztos ki akar lépni az Openboxból?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Kilépés az Openboxból"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Névtelen ablak"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Kilövés..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Nem válaszol"
+
+#: openbox/client.c:3539
+#, 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 ""
+"A(z) \"%s\" ablak nem válaszol. Erőltessük a kilépést a %s jelzés küldésével?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Folyamat vége"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr "A(z) \"%s\" ablak nem válaszol. Lekapcsoljuk az X kiszolgálóról?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Lekapcsolódás"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Ugrás..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Munkaasztal-kezelés"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "Új munk_aasztal"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "Utolsó munkaasztal _eltávolítása"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Ablakok"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Munkaasztalok"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Összes munkaasztal"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Réteg"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Mindig _felül"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normál"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Mindig _alul"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "Munkaasztalra _küldés"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Kliens menü"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "_Visszaállítás"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "Ã_thelyezés"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Ãtmérete_zés"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Iko_nizálás"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximalizálás"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "_Görgetés fel/le"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "_Dekoráció eltávolítása"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Bezárás"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Érvénytelen környezet az egér hozzárendeléseknél: \"%s\""
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Érvénytelen gomb a konfigurációs fájlban \"%s\""
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Nem lehet létrehozni a könyvtárat '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Bezárás"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Ütköző billentyű hozzárendelések a konfigurációs fájlban"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Nem található ilyen érvényes menüfájl: \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Nem sikerült végrehajtani a parancsot a csővezeték-menüben \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Érvnytelen kimenet a csővezeték-menüből \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "\"%s\" menü elérésére történt kísérlet, de az nem létezik"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Tovább..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Érvénytelen gomb \"%s\" az egér hozzárendeléseknél"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Nem lehet a saját könyvtárba váltani \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr ""
+"Nem sikerült megnyitni a DISPLAY környezeti változóban beállított képernyőt."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Nem sikerült előkészíteni az obrender programkönyvtárat."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "Az X-kiszolgáló nem támogatja ezt a nemzetközi beállítást."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr ""
+"Nem lehet beállítani a nemzetközi beállítás-módosítókat az X-kiszolgálón."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Nem található érvényes konfigurációs fájl, ezért egyszerű alapértelmezés "
+"lesznek használva"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Nem lehet betölteni témát."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Egy vagy több XML szintaktikai hiba található az Openbox konfigurációs fájl "
+"feldolgozásakor. További információkért tekintse meg a szabványos kimenetet. "
+"Az utolsó hiba ebben a fájlban volt: \"%s\" (%d sor). A hibaüzenet: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox szintaktikai hiba"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Az újraindítás során ez az új program nem volt indítható \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Használat: openbox [opciók]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Opciók:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Súgó megjelenítése és kilépés\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Verzió kiírása és kilépés\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Jelenleg futó ablakkezelő cseréje\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file FÃJL A használandó konfigurációs fájl útvonalának megadása\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Ne csatlakozzon a munkamenet-kezelőhöz\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Üzenet küldése a futó Openbox példánynak:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Openbox beállításának újratöltése\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Openbox újraindítása\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Kilépés az Openboxból\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Hibakeresési opciók:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Futtatás szinkron módban\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Hibakeresési kimenet megjelenítése\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Fókuszkezelésre vonatkozó hibakeresési kimenetek "
+"megjelenítése\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Képernyő felosztása két ál-xinerama képernyőre\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Kérjük a hibákat itt jelentse: %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s használatakor paraméter megadása kötelező\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Érvénytelen parancssori opció: \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Már fut egy ablakkezelő ezen a képernyőn: %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Nem lehet ablakkezelőt váltani ezen a képernyőn: %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Nem lép ki az ablakkezelő ezen a képernyőn: %d"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Az Openbox %d munkaasztal használatára lett beállítva, de a jelenlegi "
+"munkamenetnek %d van. Felülbíráljuk az Openbox beállítását."
+msgstr[1] ""
+"Az Openbox %d munkaasztal használatára lett beállítva, de a jelenlegi "
+"munkamenetnek %d van. Felülbíráljuk az Openbox beállítását."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "%i. munkaasztal"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "%s futtatása"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr ""
+"Érvénytelen módosító billentyű \"%s\" billentyű vagy egér hozzárendelésnél"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Érvénytelen billentyűkód \"%s\" billentyű hozzárendelésnél"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Érvénytelen billentyűnév \"%s\" billentyű hozzárendelésnél"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "A kért billentyű \"%s\" nem létezik a képernyőn"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Kilépés az Openboxból"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file használatakor paraméter megadása kötelező\n"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Nem lehet menteni ide a futó munkamenetet \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Hiba a munkamenet mentése közben \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Nincs kapcsolódva a munkamenet-kezelőhöz"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X-hiba: %s"
diff --git a/po/it.po b/po/it.po
new file mode 100644
index 0000000..a8fce8c
--- /dev/null
+++ b/po/it.po
@@ -0,0 +1,520 @@
+# Italian translation for Openbox
+# Copyright (C) 2007-2009 Davide Truffa
+# Copyright (C) 2008 Andrea Scarpino
+# This file is distributed under the same license as the openbox package.
+# Davide Truffa <davide@catoblepa.org>, 2007-2009.
+# Andrea Scarpino <bash.lnx@gmail.com>, 2008.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7.2\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2009-02-25 11:29+0100\n"
+"Last-Translator: Davide Truffa <davide@catoblepa.org>\n"
+"Language-Team: Italian <tp@lists.linux.it>\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "L'operazione \"%s\" non è valida. L'operazione non esiste."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "No"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Si"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Esegui"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Impossibile convertire il percorso utf8 \"%s\""
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Annulla"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Esci"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Sei sicuro di voler uscire?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Esci"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Sei sicuro di voler uscire da Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Chiudi Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Finestra senza nome"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Termino..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Non Risponde"
+
+#: openbox/client.c:3539
+#, 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 ""
+"La finestra \"%s\" sembra non rispondere. Vuoi terminarne l'esecuzione "
+"inviando il segnale %s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Termina Processo"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"La finestra \"%s\" non sembra rispondere. Vuoi terminarne l'esecuzione "
+"tramite il server X?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Disconnesso"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Vai a..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Gestisci i desktop"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Aggiungi un nuovo desktop"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Rimuovi l'ultimo desktop"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Finestre"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Desktop"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Tutti i desktop"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Livello"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Sempre _sopra"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normale"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Sempre s_otto"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "Invia al _desktop"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Menù della finestra"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "_Ripristina"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Muovi"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "R_idimensiona"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Mi_nimizza"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ssimizza"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "A_rrotola"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Si/No _Decorazioni"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Chiudi"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Il contesto \"%s\" specificato nelle associazioni mouse non è valido"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Il pulsante \"%s\" specificato nel file di configurazione non è valido"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Impossibile creare la directory '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Chiudi"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr ""
+"Conflitto con la scorciatoia da tastiera specificata nel file di "
+"configurazione"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Impossibile trovare il file di menù \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Impossibile eseguire il comando nel pipe-menù \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Output del pipe-menù \"%s\" non valido"
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Tentativo di accedere al menù \"%s\". Il menù non esiste"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Altri..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Il pulsante \"%s\" specificato nelle associazioni mouse non è valido"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Impossibile accedere alla directory home \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Impossibile accedere al display specificato nella variabile DISPLAY."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Impossibile inizializzare la libreria obrender."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "Il server X non ha il supporto per la localizzazione."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr ""
+"Impossibile impostare i tasti modificatori localizzati per il server X."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Impossibile trovare un file di configurazione valido, verranno utilizzate le "
+"impostazioni predefinite"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Impossibile caricare un tema."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Sono stati trovati uno o più errori nel file di configurazione di Openbox. "
+"Vedi stdout per ulteriori informazioni. L'ultimo errore era in \"%s\" alla "
+"linea %d, con il messaggio: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Errore di sintassi"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Non è stato possibile riavviare il nuovo eseguibile \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Sintassi: openbox [opzioni]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Opzioni:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Mostra questo messaggio di aiuto ed esce\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Mostra il numero di versione ed esce\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Sostituisce l'attuale window manager attivo\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file FILE Specifica il percorso del file di configurazione\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Disabilita la connessione al session manager\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Inviare messaggi ad un'istanza di Openbox attiva:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Ricarica la configurazione di Openbox\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Riavvia Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Termina Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Opzioni di debug:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Esegue in modalità sincrona\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Mostra le informazioni di debug\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Mostra le informazioni di debug sulla gestione del "
+"focus\n"
+
+#: openbox/openbox.c:547
+#, fuzzy
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+" --debug-session Mostra le informazioni di debug sulla gestione del "
+"session\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Divide lo schermo per simulare xinerama\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Segnalate eventuali bug a %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s richiede un argomento\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Argomento da linea di comando non valido \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Un window manager è già attivo sullo schermo %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Impossibile acquisire la selezione del window manager sullo schermo %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Il WM sullo schermo %d non è terminato"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox è configurato per %d desktop, ma la sessione attuale ne ha %d. "
+"Ignoro la configurazione di Openbox."
+msgstr[1] ""
+"Openbox è configurato per %d desktop, ma la sessione attuale ne ha %d. "
+"Ignoro la configurazione di Openbox."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "desktop %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Sto eseguendo %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr ""
+"Il nome del tasto \"%s\" specificato nelle associazioni di mouse/tastiera "
+"non è valido"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr ""
+"Il codice tastiera \"%s\" specificato nelle associazioni di mouse/tastiera "
+"non è valido"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr ""
+"Il nome del tasto \"%s\" specificato nelle associazioni di mouse/tastiera "
+"non è valido"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Il tasto richiesto \"%s\" non esiste sul display"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "Ok"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Chiudi Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file richiede un argomento\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "L'azione SessionLogout non è disponibile se Openbox è compilato senza il "
+#~ "supporto del gestore delle sessioni."
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Impossibile salvare la sessione in \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Errore durante il salvataggio della sessione in \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Non connesso al gestore di sessioni"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Errore del server X: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Impossibile eseguire il comando \"%s\": %s"
diff --git a/po/ja.po b/po/ja.po
new file mode 100644
index 0000000..132b887
--- /dev/null
+++ b/po/ja.po
@@ -0,0 +1,502 @@
+# Japanese messages for openbox.
+# Copyright (C) 2004 Mikael Magnusson
+# This file is distributed under the same license as the Openbox package.
+# Yukihiro Nakai <nakai@gnome.gr.jp>, 2003.
+# Ryoichiro Suzuki <ryoichiro.suzuki@gmail.com>, 2007, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7\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-04 16:32+0100\n"
+"Last-Translator: Ryoichiro Suzuki <ryoichiro.suzuki@gmail.com>\n"
+"Language-Team: Japanese <ja@li.org>\n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr ""
+"ä¸æ­£ãªã‚¢ã‚¯ã‚·ãƒ§ãƒ³\"%s\"ãŒè¦æ±‚ã•ã‚Œã¾ã—ãŸã€‚ãã®ã‚ˆã†ãªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯å­˜åœ¨ã—ã¾ã›ã‚“。"
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "ã„ã„ãˆ"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "ã¯ã„"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "実行ã™ã‚‹"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "パス\"%s\"ã‚’ utf8 ã‹ã‚‰å¤‰æ›ã™ã‚‹ã®ã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "キャンセル"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "終了"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "ログアウトã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "ログアウト"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Openbox を終了ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Openbox を終了ã™ã‚‹"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "å称未設定"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "強制終了中..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "応答ãªã—"
+
+#: openbox/client.c:3539
+#, 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 ""
+"ウィンドウ \"%s\" ã¯å¿œç­”ã—ã¦ã„ãªã„よã†ã§ã™ã€‚%s ä¿¡å·ã‚’é€ã‚Šå¼·åˆ¶çµ‚了ã—ã¾ã™ã‹ï¼Ÿ"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "プロセスを終了ã™ã‚‹"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr "ウィンドウ \"%s\" ã¯å¿œç­”ã—ã¦ã„ãªã„よã†ã§ã™ã€‚Xサーãƒã‹ã‚‰åˆ‡æ–­ã—ã¾ã™ã‹ï¼Ÿ"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "切断ã™ã‚‹"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "移動ã™ã‚‹..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "デスクトップを管ç†"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "æ–°ã—ãデスクトップを追加(_A)"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "最後ã®ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—を削除(_R)"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "ウィンドウ"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "デスクトップ"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "ã™ã¹ã¦ã®ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—(_A)"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "階層(_L)"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "常ã«æœ€ä¸Šå±¤ã«ã™ã‚‹(_T)"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "通常(_N)"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "常ã«æœ€ä¸‹å±¤ã«ã™ã‚‹(_B)"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "デスクトップã«é€ã‚‹(_S)"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "クライアントメニュー"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "復元(_E)"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "移動(_M)"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "サイズã®å¤‰æ›´(_Z)"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "最å°åŒ–(_N)"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "最大化(_X)"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "å·»ã上ã’/展開(_R)"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "éž/装飾(_D)"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "é–‰ã˜ã‚‹(_C)"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "マウス割り当ã¦ã«æ–¼ã„ã¦ä¸æ­£ãªã‚³ãƒ³ãƒ†ã‚¯ã‚¹ãƒˆ \"%s\""
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "ä¸æ­£ãªãƒœã‚¿ãƒ³\"%s\"ãŒè¨­å®šãƒ•ã‚¡ã‚¤ãƒ«ã§æŒ‡å®šã•ã‚Œã¦ã„ã¾ã™ã€‚"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "ディレクトリ'%s'を作れã¾ã›ã‚“: %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "é–‰ã˜ã‚‹"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "設定ファイルã«ã‚­ãƒ¼å‰²ã‚Šå½“ã¦ã®è¡çªãŒã‚ã‚Šã¾ã™ã€‚"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "正当ãªãƒ¡ãƒ‹ãƒ¥ãƒ¼ãƒ•ã‚¡ã‚¤ãƒ«\"%s\"を見ã¤ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "パイプメニューã®ç‚ºã®ã‚³ãƒžãƒ³ãƒ‰\"%s\"ã®å®Ÿè¡Œã«å¤±æ•—ã—ã¾ã—ãŸ: %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "パイプメニュー\"%s\"ã‹ã‚‰ã®ä¸æ­£ãªå‡ºåŠ›ã§ã™ã€‚"
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "メニュー\"%s\"ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’試ã¿ã¾ã—ãŸãŒã€ãã‚Œã¯å­˜åœ¨ã—ã¾ã›ã‚“。"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "ã‚‚ã£ã¨..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "マウス割り当ã¦ã«æ–¼ã„ã¦ä¸æ­£ãªãƒœã‚¿ãƒ³ \"%s\""
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "ホームディレクトリ\"%s\"ã«ç§»å‹•ã§ãã¾ã›ã‚“: %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "環境変数 DISPLAY ã‹ã‚‰ãƒ‡ã‚£ã‚¹ãƒ—レイを開ãã®ã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "obrender ライブラリã®åˆæœŸåŒ–ã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "Xサーãƒã¯ãƒ­ã‚±ãƒ¼ãƒ«ã‚’サãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。"
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Xサーãƒã®ç‚ºã®ãƒ­ã‚±ãƒ¼ãƒ«ä¿®é£¾å­ã‚’設定ã§ãã¾ã›ã‚“。"
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr "正当ãªè¨­å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’見ã¤ã‘られã¾ã›ã‚“。å˜ç´”ãªåˆæœŸè¨­å®šã‚’使ã„ã¾ã™ã€‚"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "テーマを読ã¿è¾¼ã‚ã¾ã›ã‚“。"
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Openbox ã®è¨­å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’解æžä¸­ã« XML ã®æ§‹æ–‡ã‚¨ãƒ©ãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸã€‚詳ã—ã„情報"
+"ã¯æ¨™æº–出力を見ã¦ä¸‹ã•ã„。最後ã«è¦‹ã¤ã‹ã£ãŸã‚¨ãƒ©ãƒ¼ã¯\"%s\"ファイルã®%d 行目ã§ã€èª¬"
+"明ã¯ã“ã†ã§ã™ï¼š%s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox 構文エラー"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "å†èµ·å‹•ã®éš›æ–°ã—ã„実行ファイル\"%s\"ã®å®Ÿè¡Œã«å¤±æ•—ã—ã¾ã—ãŸ: %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "著作権 (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "用法: openbox [オプション]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"オプション:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help ã“ã®ä½¿ã„方を表示ã—ã¦çµ‚了ã—ã¾ã™\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’表示ã—ã¦çµ‚了ã—ã¾ã™\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace ç¾åœ¨å®Ÿè¡Œä¸­ã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãƒžãƒãƒ¼ã‚¸ãƒ£ã‚’ç½®ãæ›ãˆã¾ã™\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file FILE 使用ã™ã‚‹è¨­å®šãƒ•ã‚¡ã‚¤ãƒ«ã®ãƒ‘スを指定ã—ã¾ã™\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable セッションマãƒãƒ¼ã‚¸ãƒ£ã¸ã®æŽ¥ç¶šã‚’æ­¢ã‚ã¾ã™\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"実行中㮠Openbox ã«å‘½ä»¤ã‚’é€ã‚Šã¾ã™:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Openbox ã®è¨­å®šã‚’å†èª­ã¿è¾¼ã¿ã—ã¾ã™\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Openbox ã‚’å†èµ·å‹•ã—ã¾ã™\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Openbox を終了ã—ã¾ã™\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"デãƒãƒƒã‚°ã‚ªãƒ—ション:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync åŒæœŸãƒ¢ãƒ¼ãƒ‰ã§å®Ÿè¡Œã—ã¾ã™\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug デãƒãƒƒã‚°æƒ…報を表示ã—ã¾ã™\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus フォーカスã®æ‰±ã„ã«é–¢ã™ã‚‹ãƒ‡ãƒãƒƒã‚°æƒ…報を表示ã—ã¾ã™\n"
+
+#: openbox/openbox.c:547
+#, fuzzy
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+" --debug-session セッションã®æ‰±ã„ã«é–¢ã™ã‚‹ãƒ‡ãƒãƒƒã‚°æƒ…報を表示ã—ã¾ã™\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama å½ã® xinerama スクリーンã«åˆ†å‰²è¡¨ç¤ºã—ã¾ã™\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"ãƒã‚°ã¯ %s å®›ã¸å ±å‘Šã—ã¦ä¸‹ã•ã„\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s requires an argument\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "ä¸æ­£ãªã‚³ãƒžãƒ³ãƒ‰ãƒ©ã‚¤ãƒ³å¼•æ•° \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "スクリーン%dã§ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãƒžãƒãƒ¼ã‚¸ãƒ£ãŒæ—¢ã«èµ·å‹•ã—ã¦ã„ã¾ã™ã€‚"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "スクリーン%dã§ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãƒžãƒãƒ¼ã‚¸ãƒ£ã®é¸æŠžã‚’å–å¾—ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "スクリーン%dã®WMãŒçµ‚了ã—ã¾ã›ã‚“。"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox 㯠%d 個ã®ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—を設定ã•ã‚Œã¾ã—ãŸãŒ, ç¾åœ¨ã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ã¯ %d 個"
+"æŒã£ã¦ã„ã¾ã™ã€‚ Openbox ã®è¨­å®šã‚’無視ã—ã¾ã™ã€‚"
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "デスクトップ%i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "起動中 %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "キー/マウス割り当ã¦ã®ä¸­ã®ä¸æ­£ãªä¿®é£¾ã‚­ãƒ¼ \"%s\""
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "キー割り当ã¦ã®ä¸­ã®ä¸æ­£ãªã‚­ãƒ¼ã‚³ãƒ¼ãƒ‰ \"%s\""
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "キー割り当ã¦ã®ä¸­ã®ä¸æ­£ãªã‚­ãƒ¼å称 \"%s\""
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "è¦æ±‚ã•ã‚ŒãŸã‚­ãƒ¼\"%s\"ã¯ãã®ãƒ‡ã‚£ã‚¹ãƒ—レイã«å­˜åœ¨ã—ã¾ã›ã‚“。"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Openbox を終了ã™ã‚‹"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file requires an argument\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "Openbox ãŒã‚»ãƒƒã‚·ãƒ§ãƒ³ç®¡ç†ã®æ©Ÿèƒ½ãªã—ã«ä½œã‚‰ã‚ŒãŸã®ã§ SessionLogout アクション"
+#~ "ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "セッションを\"%s\"ã«ä¿å­˜ã§ãã¾ã›ã‚“: %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "セッションを\"%s\"ã«ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒèµ·ãã¾ã—ãŸ: %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "セッションマãƒãƒ¼ã‚¸ãƒ£ã«æŽ¥ç¶šã•ã‚Œã¦ã„ã¾ã›ã‚“。"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Xエラー: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "\"%s\"ã®å®Ÿè¡Œã«å¤±æ•—ã—ã¾ã—ãŸ: %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "アクション\"%s\"ã®ä¸æ­£ãªä½¿ç”¨ã§ã™ã€‚ã“ã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯ç„¡è¦–ã•ã‚Œã¾ã™ã€‚"
diff --git a/po/lt.po b/po/lt.po
new file mode 100644
index 0000000..289f220
--- /dev/null
+++ b/po/lt.po
@@ -0,0 +1,496 @@
+# Lithuanian messages for openbox
+# Copyright (C) 2008 Dana Jansens
+# This file is distributed under the same license as the openbox package.
+# Vytautas (GODhack) <vytautas1987@yahoo.com>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7.2\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2008-05-04 21:16+0200\n"
+"Last-Translator: Vytautas <vytautas1987@yahoo.com>\n"
+"Language-Team: Lithuanian <lt@li.ourg>\n"
+"Language: lt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Neteisingo veiksmo \"%s\" pareikalauta. Toks veiksmas neegzistuoja."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Ne"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Taip"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Vygdyti"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Nepavyko išversti \"%s\" iš utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Atšaukti"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "IÅ¡eiti"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Ar tikrai norite išsiregistruoti?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "IÅ¡siregistruoti"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Ar tikrai norite išjungti Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "IÅ¡jungti Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Bevardis Langas"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Naikinama..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Neatsako"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Langas \"%s\" neatsako. Ar nori jį priverstinai išjungti nusiūsdamas %s "
+"signalÄ…?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Baigti procesÄ…"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr "Langas \"%s\" neatsako. Ar nori jį atjungti nuo X serverio?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Atsijungti"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Eiti ten..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Valdyti darbastalius"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Pridėti darbastalį"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "P_ašalinti paskutinį darbastalį"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Langai"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Darbastaliai"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Visi darbastaliai"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Sluoksnis"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Visada _viršuje"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normalus"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Visada _apaÄioje"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "Siūsti į _darbastalį"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Kliento menu"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "_Atstatyti"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Judinti"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "_Pakeisti dydį"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "_Mažinti"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "D_idinti"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "Apvers_ti aukštyn/žemyn"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Ne/D_ekoruoti"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Uždaryti"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Neteisingas kontekstas \"%s\" pelÄ—s nustatymuose"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Neteisingas mygtuskas \"%s\" nurodytas nustatymų byloje"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Negalima padaryti direktorijos '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Uždaryti"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Konfliktas tarp mygtukų nustatymų byloje"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Nerasta gera menu byla \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Nepavyko pipe-menu komandos vygdyti \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Neteisinga išvestis iš pipe-menu \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Bandoma įeiti į menu \"%s\", bet jis neegzistuoja"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Dar..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Neteisingas mygtukas \"%s\" pelÄ—s nustatymuose"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Negalima patekti į home direktoriją \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Nepavyko atidaryti ekrano pagal DISPLAY aplinkos kintamąjį."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Nepavyko inicijuoti obrender bibliotekos."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X serveris nepalaiko lokalÄ—s."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Negalima nustatyti lokalės keitinių X serveriui."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Nepavyksta rasti geros nustatymų bylos, naudojami standartiniai nustatymai"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Nepavyksta įjunggti temos."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"XML sintaksės klaidos rastos Openbox nustatymų bylose. Žiurėkite stdout dėl "
+"daugiau informacijos. PaskutinÄ— klaida byloje \"%s\" eilutÄ—je %d, su "
+"pranešimu: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox SintaksÄ—s Klaida"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Restartui nepavyko įjungti \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "SintaksÄ—: openbox [nustatymai]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Nustatymai:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Parodyti tai ir išeiti\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Parodyti versiją ir išeiti\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Pakeisti jau veikiantį VM\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file FILE Nurodyti kur yra nustatymų byla\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Atsijungti nuo sesijos tvarkyklÄ—s\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Perduodamos žinutės į veikiantį Openbox:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Perkrauti Openbox nustatymus\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Perkrauti Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Išeiti iš Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Klaidų šalinimo nustatymai:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Paleisti synchronous mode\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Rodyti debugging output\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus Rodyti debugging output dÄ—l focus handling\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr " --debug-session Rodyti debugging output dÄ—l session management\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Skirstyt ekraną į netikrus xinerama ekranus\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Praneškite apie vabaliukus %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s reikalauja argumento\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Blogas komandinÄ—s eilutÄ—s argumentas \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "WM jau veikia ekrane %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Nepavyksta gauti langų tvarkyklės pasirinkimo ekrane %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "WM ekrane %d neišsijungia"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, fuzzy, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox yra suderintas %d darbastaliams, bet dabartinÄ— sesija turi %d. Mes "
+"naudosime kitą, ne Openbox, konfigūraciją."
+msgstr[1] ""
+"Openbox yra suderintas %d darbastaliams, bet dabartinÄ— sesija turi %d. Mes "
+"naudosime kitą, ne Openbox, konfigūraciją."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "darbastalis %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Veikia %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr ""
+"Neteisingas modifikavimų klavišas \"%s\" klaviatūros/pelės priskyrimuose"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Neteisingas klavišo kodas \"%s\" klavišų priskyrimuose"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Neteisingas klavišo vardas \"%s\" klavišų priskyrimuose"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Norimas klavišas \"%s\" neegzistuoja ekrane"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "IÅ¡jungti Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file reikalauja argumento\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "SessionLogout action negalimas, nes Openbox buvo pastatytas be sesijos "
+#~ "valdymo palaikymo"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Negalima išsaugoti sesijos į \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Klaida bandant išsaugot sesiją į \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Neprisijungta prie sesijų sesijos tvarkyklė"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X Klaida: %s"
diff --git a/po/lv.po b/po/lv.po
new file mode 100644
index 0000000..6c434c2
--- /dev/null
+++ b/po/lv.po
@@ -0,0 +1,503 @@
+# Latvian translations for openbox.
+# Copyright (C) 2010 Dana Jansens
+# This file is distributed under the same license as the openbox package.
+#
+# Einars Sprugis <einars8@gmail.com>, 2010.
+msgid ""
+msgstr ""
+"Project-Id-Version: 3.4.10\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2010-01-08 21:11+0200\n"
+"Last-Translator: Einars Sprugis <einars8@gmail.com>\n"
+"Language-Team: Latvian <locale@laka.lv>\n"
+"Language: lv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : "
+"2);\n"
+"X-Generator: Lokalize 1.0\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "PrasÄ«ta neatļauta darbÄ«ba \"%s\". Å Äda darbÄ«ba neeksistÄ“."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "NÄ“"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "JÄ"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Izpildīt"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "NeizdevÄs pÄrveidot ceļu \"%s\" no utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Atcelt"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Iziet"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Vai tieÅ¡Äm vÄ“laties atteikties?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Atteikties"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Vai tieÅ¡Äm vÄ“laties iziet no Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Iziet no Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Logs bez nosaukuma"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Nogalina..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Neatbild"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Logs \"%s\" neatbild. Vai vÄ“lieties to aizvÄ“rt piespiedu kÄrtÄ, nosÅ«tot "
+"signÄlu %s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Nobeigt procesu"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr "Logs \"%s\" neatbild. Vai vēlaties to atvienot no X servera?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Atvienot"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Iet uz turieni..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "PÄrvaldÄ«t darbvirsmas"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "Pievienot j_aunu darbvirsmu"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "Noņemt pēdējo da_rbvirsmu"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Logi"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Darbvirsmas"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Visas darbvirsmas"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "S_lÄnis"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "VienmÄ“r augÅ¡Ä"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_NormÄls"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "VienmÄ“r a_pakÅ¡Ä"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "No_sūtīt uz darbvirsmu"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Klienta izvēlne"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "Atja_unot"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "PÄrviet_ot"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Mainīt i_zmēru"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Mi_nimizēt"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Maksimizē_t"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "Sa_ritinÄt/AtritinÄt"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Bez/Ar _dekorÄcijÄm"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "Ai_zvērt"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Neatļauts konteksts \"%s\" peles saīsnē"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "KonfigurÄcijas failÄ \"%s\" norÄdÄ«ts neatļauts taustiņš"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Nevarēja izveidot mapi '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Aizvērt"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "KonfliktÄ“ ar tastatÅ«ras saÄ«snÄ“m konfigurÄcijas failÄ"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Nav atrasts atļauts izvēlnes fails \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "NevarÄ“ja izpildÄ«t skriptÄ“tÄs izvÄ“lnes komandu \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Neatļauta izvade no skriptÄ“tÄs izvÄ“lnes \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "MÄ“Ä£inÄja piekļūt izvÄ“lnei \"%s\", bet tÄ neeksistÄ“"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "VairÄk..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Neatļauts taustiņš \"%s\" peles saīsnē"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "NevarÄ“ja pÄriet uz mÄjas mapi \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "NeizdevÄs atvÄ“rt displeju no DISPLAY vides mainÄ«gÄ."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "NeizdevÄs inicializÄ“t obrender bibliotÄ“ku."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X serveris neatbalsta lokÄli."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Nevar uzstÄdÄ«t lokÄles modificÄ“tÄjus X serverim."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"NevarÄ“ja atrast atļautu konfigurÄcijas failu, tiek izmantoti noklusÄ“jumi"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Nebija iespÄ“jams ielÄdÄ“t tÄ“mu."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"AnalizÄ“jot Openbox konfigurÄcijas failus, tika atrastas viena vai vairÄkas "
+"XML sintakses kļūdas. AplÅ«kojiet standarta izvadi, lai noskaidrotu vairÄk. "
+"PÄ“dÄ“jÄ kļūda bija failÄ \"%s\" - %d rinda, kļūdas ziņojums: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox sintakses kļūda"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "PÄrstartÄ“tÄjam neizdevÄs palaist jauno izpildÄmo \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Autortiesības (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Sintakse: openbox [opcijas]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Opcijas:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help ParÄda Å¡o palÄ«dzÄ«bas tekstu un iziet\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version ParÄda versiju un iziet\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Aizvieto paÅ¡reiz palaisto logu pÄrvaldnieku\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file FAILS NorÄda ceļu uz izmantojamo konfigurÄcijas failu\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable PÄrtrauc savienojumu ar sesiju pÄrvaldnieku\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Nodod ziņojumus esošai Openbox instancei:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure PÄrlÄdÄ“ Openbox konfigurÄcijas failus\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart PÄrstartÄ“ Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Iziet no Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Atkļūdošanas iespējas:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Palaist sinhronajÄ režīmÄ\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug RÄdÄ«t atkļūdoÅ¡anas izvadi\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus RÄdÄ«t atkļūdoÅ¡anas izvadi fokusÄ“Å¡anas darbÄ«bÄm\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr ""
+" --debug-xinerama SadalÄ«t displeju vairÄkos viltus xinerama ekrÄnos\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Lūdzu, ziņojiet kļūdas %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s vajadzīgs arguments\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Neatļauts komandrindas arguments \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Logu pÄrvaldnieks jau palaists uz %d. ekrÄna"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "NevarÄ“ja iegÅ«t logu pÄrvaldnieka izvÄ“li uz %d. ekrÄna"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Logu pÄrvaldnieks uz %d. ekrÄna nebeidz darbÄ«bu"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox ir konfigurÄ“ts %d darbvirsmai, bet paÅ¡reizÄ“jai sesijai tÄdu ir %d. "
+"Å Ä« Openbox konfigurÄcijas opcija tiks ignorÄ“ta."
+msgstr[1] ""
+"Openbox ir konfigurÄ“ts %d darbvirsmÄm, bet paÅ¡reizÄ“jai sesijai tÄdu ir %d. "
+"Å Ä« Openbox konfigurÄcijas opcija tiks ignorÄ“ta."
+msgstr[2] ""
+"Openbox ir konfigurÄ“ts %d darbvirsmÄm, bet paÅ¡reizÄ“jai sesijai tÄdu ir %d. "
+"Å Ä« Openbox konfigurÄcijas opcija tiks ignorÄ“ta."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "darbvirsma %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Palaiž %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Neatļauts modificÄ“tÄjtaustiņš \"%s\" tastatÅ«ras/peles saÄ«snÄ“"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Neatļauts taustiņa kods \"%s\" tastatūras saīsnē"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Neatļauts taustiņa nosaukums \"%s\" tastatūras saīsnē"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Pieprasītais taustiņš \"%s\" uz displeja neeksistē"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "Labi"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Iziet no Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file vajadzīgs arguments\n"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "NeizdevÄs saglabÄt sesiju \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Kļūda saglabÄjot sesiju \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Nav savienots ar sesiju pÄrvaldnieku"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X kļūda: %s"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "Darbība 'SessionLogout' nav pieejama, jo Openbox ir kompilēts bez sesiju "
+#~ "pÄrvaldÄ«bas atbalsta"
diff --git a/po/nl.po b/po/nl.po
new file mode 100644
index 0000000..b869348
--- /dev/null
+++ b/po/nl.po
@@ -0,0 +1,504 @@
+# Dutch translations for openbox.
+# This file is distributed under the same license as the openbox package.
+#
+# Mark Pustjens <pustjens@dds.nl>, 2007.
+# Jochem Kossen <jkossen@xs4all.nl>, 2007.
+# Marvin Vek <laen@onedot.nl>, 2008.
+# Benno Schulenberg <benno@vertaalt.nl>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7\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-18 23:30+0100\n"
+"Last-Translator: Benno Schulenberg <benno@vertaalt.nl>\n"
+"Language-Team: Dutch <vertaling@vrijschrift.org>\n"
+"Language: nl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Ongeldige actie \"%s\" werd gevraagd. Deze actie bestaat niet."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Nee"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Ja"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Uitvoeren"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Converteren van het pad \"%s\" vanuit UTF-8 is mislukt"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Annuleren"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Afsluiten"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Weet u zeker dat u wilt uitloggen?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Uitloggen"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Weet u zeker dat u Openbox wilt afsluiten?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Openbox afsluiten"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Naamloos venster"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Termineren..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Reageert niet"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Het venster \"%s\" reageert niet. Wilt u het afsluiten forceren door het "
+"signaal %s te sturen?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Beëindig proces"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"Het venster \"%s\" reageert niet. Wilt u de verbinding van het venster met "
+"de X-server verbreken?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Verbreek verbinding"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Ga hierheen..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Bureaubladen beheren"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Voeg nieuw bureaublad toe"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "V_erwijder laatste bureaublad"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Vensters"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Bureaubladen"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Alle bureaubladen"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Laag"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Altijd _bovenop"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normaal"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Altijd _onderop"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "Verplaats _naar bureaublad"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Venstermenu"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "_Herstellen"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Verplaatsen"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "_Grootte aanpassen"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "_Iconificeren"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "_Maximaliseren"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "_Op/neerklappen"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "_Vensterrand weghalen/toevoegen"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Sluiten"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Ongeldige context \"%s\" in muisbinding"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Ongeldige knop \"%s\" opgegeven in het configuratiebestand"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Kan map '%s' niet aanmaken: %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Sluiten"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Conflict in toetsbindingen in het configuratiebestand"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Kan geen geldig menubestand \"%s\" vinden"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Uitvoeren van commando voor pipe-menu \"%s\" is mislukt: %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Ongeldige uitvoer van pipe-menu \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Toegang tot niet-bestaand menu \"%s\" werd gevraagd"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Meer..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Ongeldige knop \"%s\" in muisbinding"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Kan thuismap \"%s\" niet de huidige map maken: %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Kan scherm genoemd in omgevingsvariabele DISPLAY niet openen."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Initialiseren van de 'obrender'-bibliotheek is mislukt."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X-server ondersteunt lokalisatie niet."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Kan lokalisatie voor de X-server niet instellen."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Kan geen geldig configuratiebestand vinden; simpele standaardinstellingen "
+"worden gebruikt"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Kan geen thema laden."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Een of meer XML-syntaxfouten zijn gevonden tijdens het inlezen van de "
+"Openbox-configuratiebestanden. Zie standaarduitvoer voor meer informatie. "
+"De laatste fout werd gevonden in bestand \"%s\" regel %d, met bericht: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox-syntaxfout"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Uitvoeren van nieuw programma \"%s\" bij herstart is mislukt: %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Gebruik: openbox [opties]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Opties:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Deze hulptekst tonen en stoppen\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Versie tonen en stoppen\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace De draaiende vensterbeheerder vervangen\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file BESTAND\n"
+" Dit configuratiebestand gebruiken\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Verbinding met de sessiebeheerder uitschakelen\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Berichten worden naar een draaiende Openbox-instantie gestuurd:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Openbox-configuratie opnieuw laden\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Openbox herstarten\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Openbox afsluiten\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Debugging-opties:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync In synchrone modus starten\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Debuguitvoer weergeven\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus Debuguitvoer voor focusafhandeling weergeven\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Het scherm in nep Xinerama-schermen splitsen\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Rapporteer programmafouten aan %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s vereist een argument\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Onbekende optie \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Er draait al een vensterbeheerder op scherm %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Kan op scherm %d geen vensterbeheerderselectie verkrijgen"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "De vensterbeheerder op scherm %d sluit zichzelf niet af"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox is geconfigureerd voor %d bureaublad, maar de huidige sessie heeft "
+"er %d. De Openbox-instelling wordt genegeerd."
+msgstr[1] ""
+"Openbox is geconfigureerd voor %d bureaubladen, maar de huidige sessie heeft "
+"er %d. De Openbox-instelling wordt genegeerd."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "bureaublad %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Starten van %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Ongeldige modificatietoets \"%s\" in toetsen-/muisbinding"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Ongeldige toetscode \"%s\" in toetsenbinding"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Ongeldige toetsnaam \"%s\" in toetsenbinding"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Gevraagde toets \"%s\" bestaat niet op het scherm"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Openbox afsluiten"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "'--config-file' vereist een argument\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "De actie 'SessionLogout' is niet beschikbaar omdat Openbox gecompileerd "
+#~ "is zonder ondersteuning voor sessiebeheer."
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Kan de sessie niet opslaan in \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Fout tijdens het opslaan van de sessie in \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Niet met een sessiebeheerder verbonden"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X-fout: %s"
diff --git a/po/no.po b/po/no.po
new file mode 100644
index 0000000..0d01353
--- /dev/null
+++ b/po/no.po
@@ -0,0 +1,506 @@
+# Norwegian messages for openbox
+# Copyright (C) 2007 Dana Jansens
+# This file is distributed under the same license as the openbox package.
+#
+# Michael Kjelbergvik Thung <postlogic@gmail.com>, 2008.
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7\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:37+0100\n"
+"Last-Translator: Michael Kjelbergvik Thung <postlogic@gmail.com>\n"
+"Language-Team: None\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Ugyldig operasjon \"%s\" etterspurt. Operasjonen finnes ikke."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Nei"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Ja"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Utfør"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Feil ved konvertering av \"%s\" fra utf8 "
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Avbryt"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Avslutt"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Er du sikker på at du vil logge ut?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Logg Ut"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Er du sikker på at du vil avslutte Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Avslutt Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Ukjent Vindu"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Dreper..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Svarer Ikke"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Vinduet \"%s\" svarer ikke. Vil du utføre tvunget avslutning ved å sende "
+"signalet %s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Avslutt Prosess"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"Vinduet \"%s\" svarer ikke. Vil du fjerne tilknytning av vinduet til X-"
+"serveren?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Fjern tilknytning"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "GÃ¥ dit..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Behandle skrivebord"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Nytt skrivebord"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Fjern siste skrivebord"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Vinduer"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Skrivebord"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Alle skrivebord"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "La_g"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Alltid ø_verst"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "Nor_mal"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Alltid _nederst"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "_Send til"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Klient-meny"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "Tilbak_estill"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Flytt"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Endre s_tørrelse"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "_Minimer"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximer"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "_Rull opp/ned"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Fjern/Legg til _dekorasjon"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Lukk"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Ugyldig innhold \"%s\" i binding for mus"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Ugyldig tast \"%s\" spesifisert i konfigurasjonsfilen"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Kan ikke lage katalog '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Lukk"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Konflikt med hurtigtastbinding i konfigurasjonsfilen"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Kan ikke finne en gyldig menyfil \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Kunne ikke kjøre kommando for pipe-meny \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Ugyldig utdata fra pipe-menyen \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Forsøkte å åpne menyen \"%s\", men denne finnes ikke"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Mer..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Ugyldig knapp \"%s\" i binding for mus"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Kan ikke endre til hjemmekatalogen \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Kunne ikke åpne displayet fra DISPLAY-miljøvariabelen"
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Kunne ikke starte obrender-biblioteket."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X-serveren støtter ikke lokalisering."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Kan ikke stille inn lokaliseringsmodifikatorene for X-serveren."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr "Kunne ikke finne en gyldig konfigurasjonsfil, bruker standardverdier"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Kan ikke laste et tema."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"En eller flere XML-syntaksfeil ble funnet ved lesing av konfigurasjonsfilene "
+"til Openbox. Se stdout for mer informasjon. Forrige feil funnet var i filen "
+"\"%s\", linje %d, med beskjeden: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox Syntaksfeil"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Kunne ikke starte nytt program ved omstart: \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Syntax: openbox [alternativer\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Alternativ:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Vise denne hjelpeteksten og avslutt\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Vis versjonsnummeret og avslutt\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Erstatt den kjørende vindusbehandleren\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file FIL Spesifisér filbane til konfigurasjonsfilen du vil ta i "
+"bruk\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Deaktiver tilknytning til sesjonsbehandleren\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Sender beskjeder til en kjørende Openbox-instans:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Oppdater Openbox' konfigurasjon\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Start Openbox på nytt\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Avslutt Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Debug-alternativ:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Kjør i synkron-modus\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Vis debuggingsinformasjon\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus Vis debuggingsinformasjon for fokus-håndtering\n"
+
+#: openbox/openbox.c:547
+#, fuzzy
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+" --debug-session Vis debuggingsinformasjon for session-håndtering\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " -debug-xinerama Splitt displayet for falske xinerama-skjermer\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Vennligst rapporter bugs til %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s krever et argument\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Ugyldig kommandolinje-argument \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "En vindusbehandler kjører allerede på skjerm %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Kunne ikke hendte vindusbehandlerens markering på skjerm %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Vindusbehandleren på skjerm %d vil ikke avslutte"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, fuzzy, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Aktiv sesjon har %2$d skrivebord, mens Openbox er konfigurert til %1$d. "
+"Benytter innstillingene for den aktive sesjonen."
+msgstr[1] ""
+"Aktiv sesjon har %2$d skrivebord, mens Openbox er konfigurert til %1$d. "
+"Benytter innstillingene for den aktive sesjonen."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "skrivebord %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Kjører %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Ugyldig modifikasjonsknapp \"%s\" i binding for tast/mus"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Ugyldig tastekode \"%s\" i hurtigtastbinding"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Ugyldig tastenavn \"%s\" i hurtigtastbinding"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Ønsket tast \"%s\" eksisterer ikke i displayet"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Avslutt Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file krever et argument\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "SessionLogout er ikke tilgjengelig fordi Openbox ble kompilert uten "
+#~ "støtte for sesjonsbehandling"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Kan ikke lagre sesjon til \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Feil ved lagring av sesjon til \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Ikke tilknyttet en sesjonsbehandler"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Feil i X: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Kunne ikke kjøre \"%s\": %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "Ugyldig bruk av aksjonen \"%s\". Aksjonen vil bli ignorert."
diff --git a/po/openbox.pot b/po/openbox.pot
new file mode 100644
index 0000000..785d49e
--- /dev/null
+++ b/po/openbox.pot
@@ -0,0 +1,453 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR Dana Jansens
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr ""
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr ""
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr ""
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr ""
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr ""
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr ""
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr ""
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr ""
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr ""
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr ""
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr ""
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr ""
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr ""
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr ""
+
+#: openbox/client.c:3539
+#, 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 ""
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr ""
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr ""
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr ""
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr ""
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr ""
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr ""
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr ""
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr ""
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr ""
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr ""
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr ""
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr ""
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr ""
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr ""
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr ""
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr ""
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr ""
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr ""
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr ""
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr ""
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr ""
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr ""
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr ""
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr ""
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr ""
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr ""
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr ""
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr ""
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr ""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr ""
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr ""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr ""
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr ""
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr ""
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr ""
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr ""
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr ""
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr ""
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr ""
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr ""
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr ""
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr ""
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr ""
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr ""
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr ""
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr ""
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr ""
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr ""
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr ""
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr ""
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr ""
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr ""
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr ""
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr ""
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr ""
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr ""
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr ""
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr ""
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr ""
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+msgstr[1] ""
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr ""
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr ""
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr ""
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr ""
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr ""
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr ""
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr ""
diff --git a/po/pl.po b/po/pl.po
new file mode 100644
index 0000000..794d0f5
--- /dev/null
+++ b/po/pl.po
@@ -0,0 +1,496 @@
+# Polish translation of Openbox 3.
+# Copyright (C) 2007 Mikael Magnusson
+# This file is distributed under the same license as the Openbox 3 package.
+# Madej <madej@afn.no-ip.org>, 2004.
+# Paweł Rusinek <p.rusinek@gmail.com>, 2007.
+# Piotr DrÄ…g <raven@pmail.pl>, 2007.
+# Jakub Åojewski <lojewski@ovi.com>, 2010.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.3\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2010-03-11 13:55+0100\n"
+"Last-Translator: Jakub Åojewski <lojewski@ovi.com>\n"
+"Language-Team: Polish <pl@li.org>\n"
+"Language: pl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Wywołana akcja \"%s\" nie istnieje."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Nie"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Tak"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Wykonaj"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Nie można przekonwertować ścieżki \"%s\" z UTF-8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Anuluj"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Wyjście"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Czy na pewno chcesz się wylogować?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Wyloguj"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Czy na pewno chcesz opuścić Openboksa?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Opuść Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Okno bez nazwy"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Kończenie..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Nie odpowiada"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Okno \"%s\" nie odpowiada. Czy wymusić zakończenie poprzez wysłanie sygnału "
+"%s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Zakończ proces"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr "Okno \"%s\" nie odpowiada. Odłączyć je od serwera X?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Odłącz"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Przejdź..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "ZarzÄ…dzaj pulpitami"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "Dod_aj nowy pulpit"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Usuń ostatni pulpit"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Okna"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Pulpity"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Wszystkie pulpity"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Warstwa"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Zawsze na _wierzchu"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normalnie"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Zawsze pod _spodem"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "Wyślij na p_ulpit"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Menu klienta"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "P_rzywróć"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Przesuń"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Zmień _rozmiar"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Zmi_nimalizuj"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Zma_ksymalizuj"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "_Zwiń/Rozwiń"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Wyświetl/ukryj _dekoracje"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "Z_amknij"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Nieprawidłowy kontekst \"%s\" w skrócie myszy"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Nieprawidłowy klawisz \"%s\" określony w pliku konfiguracyjnym"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Nie można utworzyć katalogu '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Zamknij"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Konflikt skrótów klawiszowych w pliku konfiguracyjnym"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Nie można znaleźć prawidłowego pliku menu \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Wykonanie polecenia dla pipe-menu \"%s\" nie powiodło się: %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Nieprawidłowe wyjście z pipe-menu \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Spróbowano uzyskać dostęp do menu \"%s\", ale ono nie istnieje"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Więcej..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Nieprawidłowy klawisz \"%s\" w skrócie myszy"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Nie można przejść do katalogu domowego \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Otwarcie ekranu ze zmiennej środowiskowej DISPLAY nie powiodło się."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Zainicjowanie biblioteki obrender nie powiodło się."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "Serwer X nie obsługuje ustawień lokalnych."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Nie można ustawić modyfikatorów lokalnych dla serwera X."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Nie można znaleźć prawidłowego pliku konfiguracyjnego, używanie "
+"domyślnychwartości"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Nie można wczytać motywu."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Jeden lub więcej błędów składniowych XML zostało znalezionych podczas "
+"sprawdzania plików konfiguracyjnych. Zobacz stdout aby uzyskać więcej "
+"informacji. Ostatnio błąd znaleziono w pliku \"%s\" linia %d, z wiadomością: "
+"%s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Błąd składniowy Openboksa"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr ""
+"Wykonanie nowego pliku wykonywalnego \"%s\" podczas ponownego "
+"uruchomienianie powiodło się: %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Składnia: openbox [opcje]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Opcje:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Wyświetla tę pomoc i kończy\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Wyświetla wersję i kończy\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Zastępuje aktualnie działający menedżer okien\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file FILE Podaj ścieżkę do pliku konfiguracji\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Nie tworzy połączenia z menedżerem sesji\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Przekazywanie komunikatów do działającej instancji Openboksa:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Ponownie wczytuje pliki konfiguracyjne\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Ponownie uruchamia Openboksa\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Opuść Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Opcje debugowania:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Uruchamia w trybie synchronicznym\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Wyświetla informacje o debugowaniu\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Wyświetla wyjście debugowania obsługi aktywacji\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Dzieli ekran na sztuczne ekrany xineramy\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Proszę zgłaszać błędy (w języku angielskim) pod adresem %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s wymaga argumentu\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Nieprawidłowy argument wiersza poleceń \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Menedżer okien jest już uruchomiony na ekranie %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Nie można uzyskać wyboru menedżera okien na ekranie %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Menedżer okien na ekranie %d nie kończy działania"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+msgstr[1] ""
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "pulpit %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Uruchamianie %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr ""
+"Nieprawidłowy klawisz modyfikatora \"%s\" w skrócie klawiszowym lub myszy"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Nieprawidłowy kod \"%s\" w skrócie klawiszowym"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Nieprawidłowa nazwa \"%s\" w skrócie klawiszowym"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Żądany klawisz \"%s\" nie istnieje na ekranie"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Nie można zapisać sesji do \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Wystąpił błąd podczas zapisywania sesji do \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Nie podłączono do menedżera sesji"
+
+#~ msgid "X Error: %s"
+#~ msgstr "BÅ‚Ä…d X: %s"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "SessionLogout jest niedostępne, ponieważ Openbox został stworzony bez "
+#~ "wsparcia dla zarzÄ…dzania sesjÄ…"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Wykonanie \"%s\" nie powiodło się: %s"
diff --git a/po/pt.po b/po/pt.po
new file mode 100644
index 0000000..390963f
--- /dev/null
+++ b/po/pt.po
@@ -0,0 +1,503 @@
+# Portuguese messages for openbox
+# Copyright (C) 2010 Mikael Magnusson
+# This file is distributed under the same license as the openbox package.
+# Gonçalo Ferreira <gonsas@gmail.com>, 2006.
+# Pedro Beja <althaser@gmail.com>, 2007 2008 2010.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.11.2\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2010-05-14 15:51+0100\n"
+"Last-Translator: Pedro Beja <althaser@gmail.com>\n"
+"Language-Team: None\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Pedido de acção \"%s\" inválido. Não existem quaisquer acções."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Não"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Sim"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Executar"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Falha a converter o caminho \"%s\" do utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Sair"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Tem a certeza que pretende fazer log out?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Log Out"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Tem a certeza que pretende sair do Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Sair do Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Janela sem nome"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Terminando..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Não está a responder"
+
+#: openbox/client.c:3539
+#, 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 ""
+"A janela \"%s\" parece não estar a responder. Quer forçá-la a sair enviando "
+"o sinal %s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Terminar Processo"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"A janela \"%s\" parece não estar a responder. Quer desligá-la do servidor X?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Desligar"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Ir para..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Gerir áreas de trabalho"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Adicionar nova área de trabalho"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Remover última área de trabalho"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Janelas"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Ãreas de trabalho"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Todas as áreas de trabalho"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Camada"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Sempre no _topo"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normal"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Sempre no _fundo"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "Enviar para área de _trabalho"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Menu cliente"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "R_estaurar"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Mover"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Redimen_sionar"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Mi_nimizar"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximizar"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "Des/en_rolar"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Des/_Decorar"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Fechar"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Contexto inválido \"%s\" no atalho do rato"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Botão inválido \"%s\" especificado no ficheiro de configuração"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Incapaz de criar o directório '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Fechar"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Conflito com tecla de atalho no ficheiro de configuração"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Incapaz de encontrar um ficheiro de menu válido \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Falha a executar comando para o menu de processamento \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Resultado inválido do menu de processamento \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Tentou aceder ao menu \"%s\" mas ele não existe"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Mais..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Botão inválido \"%s\" no atalho do rato"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Incapaz de mudar para o directório home \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Falha ao abrir o ecrã da variável de ambiente DISPLAY."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Falha ao inicializar a biblioteca obrender"
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "O servidor X não suporta o locale."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Não é possível configurar modificadores de locale para o servidor X."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Incapaz de encontrar um ficheiro de configuração válido, usando alguns "
+"valores simples de omissão"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Incapaz de carregar o tema."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Um ou mais erros de sintaxe do XML foram encontrados enquanto analizava os "
+"ficheiros de configuração do Openbox. Veja o stdout para mais informações. "
+"O último erro visto foi no ficheiro \"%s\" linha %d, com a mensagem: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Erro de Sintaxe do Openbox"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Falha a reiniciar a execução de um novo executável \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Direitos de autor (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Sintaxe: openbox [opções]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Opções:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Mostra esta ajuda e sai\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Mostra a versão e sai\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Substitui o corrente gestor de janelas\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file ficheiro\n"
+" Especifica o caminho do ficheiro de configuração a "
+"usar\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Desactiva a ligação com o gestor de sessões\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Passando mensagens para uma instância do Openbox em execução:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Recarrega a configuração do Openbox\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Reinicia o Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --sair Sai do Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Opções de depuração:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Executa em modo sincronizado\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Mostra o resultado da depuração\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Mostra o resultado de depuração para manipulação de "
+"foco\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Divide o ecrã em falsos ecrãs xinerama\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Por favor reporte os erros em %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s requer um argumento\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Argumento inválido na linha de comandos \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Um gestor de janelas já está em execução no ecrã %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Não consegui adequirir o gestor de janelas selecionado no ecrã %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "O gestor de janelas no ecrã %d não está a fechar"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"O Openbox está configurado para %d área de trabalho, mas a sessão corrente "
+"tem %d. Sobrescrevendo a configuração do Openbox."
+msgstr[1] ""
+"O Openbox está configurado para %d áreas de trabalho, mas a sessão corrente "
+"tem %d. Sobrescrevendo a configuração do Openbox."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "área de trabalho %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Executando %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Chave modificadora inválida \"%s\" no atalho de tecla/rato"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Código chave inválido \"%s\" na tecla de atalho"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Nome de chave inválido \"%s\" na tecla de atalho"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Chave requerida \"%s\" não existe no ecrã"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Incapaz de guardar a sessão em \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Erro enquanto guardo a sessão em \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Não está ligado a um gestor de sessões"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Erro no X: %s"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "A acção SessionLogout não está disponível visto que o Openbox foi "
+#~ "construído sem suporte à gestão de sessões"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Falha a executar \"%s\": %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "Uso inválido da acção \"%s\". A acção será ignorada."
diff --git a/po/pt_BR.po b/po/pt_BR.po
new file mode 100644
index 0000000..b0c2294
--- /dev/null
+++ b/po/pt_BR.po
@@ -0,0 +1,517 @@
+# Portuguese Brazil (pt_BR) messages for openbox
+# Copyright (C) 2007 Mikael Magnusson
+# This file is distributed under the same license as the openbox package.
+# crimeboy, 2007.
+# Og Maciel <ogmaciel@gnome.org>, 2007, 2008, 2011.
+#
+msgid ""
+msgstr ""
+"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: 2011-08-01 15:26-0400\n"
+"Last-Translator: Og Maciel <ogmaciel@gnome.org>\n"
+"Language-Team: Brazilian Portuguese <gnome-l10n-br@listas.cipsga.org.br>\n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Ação inválida \"%s\" requisitada. Ação não existe."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Não"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Sim"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Executar"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Falha ao converter o caminho \"%s\" do utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Sair"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Você tem certeza que deseja sair?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Sair"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Você tem certeza que deseja sair do Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Sair do Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Janela sem nome"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Terminando..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Não Responsivo"
+
+#: openbox/client.c:3539
+#, 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 ""
+"A janela \"%s\" não está responsiva. Você deseja forçá-la a sair enviando o "
+"sinal %s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Terminar Processo"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"A janela \"%s\" não está responsiva. Você deseja desconectá-la do servidor X?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Desconectar"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Ir lá..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Gerenciar áreas de trabalho"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Adicionar nova área de trabalho"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Remover última área de trabalho"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Janelas"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Ãreas de trabalho"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Todas as áreas de trabalho"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Camada"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Sempre no _topo"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normal"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Sempre no _fundo"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "Enviar para área de _trabalho"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Menu do cliente"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "R_estaurar"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Mover"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Redimen_sionar"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Mi_nimizar"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximizar"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "(Des)en_rolar"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "(Não) _Decorar"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Fechar"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Contexto \"%s\" inválido na associação do mouse"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Botão inválido \"%s\" especificado no arquivo de configuração"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+"Openbox foi compilado sem suporte para carregamento de imagens Imlib2. "
+"Ãcones em menus não serão carregados."
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Não foi possível criar o diretório '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Fechar"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Conflito com associação de chave no arquivo de configuração"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Não foi possível encontrar um arquivo de menu \"%s\" válido"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Falha ao executar comando para menu de processamento \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Saída inválida do menu de processamento \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Tentou acessar menu \"%s\" mas ele não existe"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Mais.."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Botão inválido \"%s\" na associação do mouse"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Não foi possível mudar para o diretório pessoal \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Falha ao abrir a tela da variavel de ambiente DISPLAY"
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Falha ao iniciar a biblioteca obrender."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "Servidor X não suporta localização."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr ""
+"Não foi possível configurar modificadores de localização para o servidor X."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Não foi possível encontrar um arquivo de configuração válido, usando alguns "
+"valores padrão simples."
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Não foi possível carregar um tema."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Um ou mais erros de sintaxe de XML foram encontrados ao analisar os arquivos "
+"de configuração do Openbox. Veja a saída padrão para mais informação. O "
+"último erro relatado foi no arquivo \"%s\" linha %d, com a mensagem: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Erro de Sintaxe do Openbox"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "O comando de reiniciar falhou ao executar novo executável \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Sintaxe: openbox [opções]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Opções:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Mostra esta ajuda e sai\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Mostra a versão e sai\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Substitui o gerenciador de janelas ativo\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file ARQUIVO\n"
+" Especifica o caminho do arquivo de configuração para "
+"usar\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr ""
+" --sm-disable Desabilita conexão com o gerenciador de sessões\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Passando mensagens para uma instância do Openbox em execução:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Recarrega a configuração do Openbox\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Reinicia o Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Sai do Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Opções de depuração:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Executa em modo sincronizado\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr " --startup CMD Executa CMD depois de iniciar\n"
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Mostra saida de depuração\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Mostra saída de depuração para manipulação de foco\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+" --debug-session Mostra saída de depuração do gerenciamento da sessão\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr ""
+" --debug-xinerama Divide a exibição de telas em telas de xinerama "
+"falsas\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Por favor reporte erros em %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s requere um argumento\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Argumento de linha de comando inválido \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Um gerenciador de janelas já está em execução na tela %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr ""
+"Não foi possível adquirir a seleção do gerenciador de janelas na tela %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "O gerenciador de janelas na tela %d não está saindo"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"O Openbox está configurado para %d área de trabalho, mas a sessão atual "
+"contém %d. Sobrescrevendo a configuração do Openbox."
+msgstr[1] ""
+"O Openbox está configurado para %d áreas de trabalho, mas a sessão atual "
+"contém %d. Sobrescrevendo a configuração do Openbox."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "área de trabalho %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Executando %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Chave modificadora \"%s\" inválida na associação de tecla/mouse"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Código chave \"%s\" inválido na associação de chave"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Nome de chave \"%s\" inválido na associação de chave"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Chave requerida \"%s\" não existe na tela"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Sair do Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file requere um argumento\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "A ação SessionLogout não está disponível já que o Openbox foi compilado "
+#~ "sem suporte de gerenciamento de sessões"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Não foi possível salvar a sessão em \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Erro enquanto salvando a sessão em \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Não está conectado à um gerente de sessões"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Erro no X: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Falha ao executar \"%s\": %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "Uso inválido da ação \"%s\". Ação será ignorada."
diff --git a/po/ru.po b/po/ru.po
new file mode 100644
index 0000000..8fa7e1e
--- /dev/null
+++ b/po/ru.po
@@ -0,0 +1,501 @@
+# Russian translation of Openbox
+# Copyright (C) 2007 Mikael Magnusson
+# This file is distributed under the same license as the openbox package.
+# Alexey Remizov <alexey@remizov.pp.ru>, 2004.
+# Nikita Bukhvostov <dragon.djanic@gmail.com>, 2007.
+# Moroz Sergey L. <se.seam@gmail.com>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7.2\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2008-05-02 10:25+0200\n"
+"Last-Translator: Moroz Sergey L. <se.seam@gmail.com>\n"
+"Language-Team: None\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Запрошено неверное дейÑтвие \"%s\". Такого дейÑÑ‚Ð²Ð¸Ñ Ð½ÐµÑ‚."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Ðет"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Да"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "ЗапуÑтить"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "ÐÐµÑƒÐ´Ð°Ñ‡Ð½Ð°Ñ ÐºÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð°Ñ†Ð¸Ñ Ð¿ÑƒÑ‚Ð¸ \"%s\" из utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Отменить"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Выйти"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Ð’Ñ‹ дейÑтвительно хотите выйти?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Выход"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Ð’Ñ‹ дейÑтвительно хотите выйти из Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Выйти из Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "БезымÑнное окно"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Завершение..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Ðет ответа"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Похоже, окно \"%s\" не отвечает. Хотите принудительно поÑлать Ñигнал выхода "
+"%s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Закончить процеÑÑ"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr "Похоже, окно \"%s\" не отвечает. Хотите отключить его от Ð¥-Ñервера?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Отключить"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Перейти..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Управление рабочими Ñтолами"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "Добавить новый рабочий Ñтол(_A)"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "Удалить поÑледний рабочий Ñтол(_R)"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Окна"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Рабочие Ñтолы"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Ð’Ñе рабочие Ñтолы"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "Положение(_L)"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Ð’Ñегда наверху(_T)"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "Обычное(_N)"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Ð’Ñегда внизу(_B)"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "Отправить на рабочий Ñтол(_S)"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "КлиентÑкое меню"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "ВоÑÑтановить(_E)"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "ПеремеÑтить(_M)"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Изменить размер(_Z)"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Свернуть в значок(_N)"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "РаÑпахнуть(_X)"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "РаÑ/Ñкрутить(_R)"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "РаÑ/Декорировать(_D)"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "Закрыть(_C)"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ ÑвÑзь \"%s\" в комбинации мыши"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Ð’ файле конфигурации определена Ð½ÐµÐ²ÐµÑ€Ð½Ð°Ñ ÐºÐ½Ð¾Ð¿ÐºÐ° \"%s\""
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Ðевозможно Ñоздать директорию '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Закрыть"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Конфликтует Ñ ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ†Ð¸ÐµÐ¹ клавиш из файла конфигурации"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Ðевозможно найти ÑоответÑтвующий файл меню \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Ðеудачное выполнение команды Ð´Ð»Ñ Ð¼ÐµÐ½ÑŽ канала \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Ðеверный выход меню канала \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Попытка доÑтупа к меню \"%s\", которого не ÑущеÑтвует"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Еще..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ ÐºÐ½Ð¾Ð¿ÐºÐ° \"%s\" в комбинации мыши"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Ðевозможно изменить на домашнюю директорию \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Ðевозможно открыть Ñкран из переменной Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸Ñ DISPLAY."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Ðевозможно запуÑтить библиотеку obrender."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X Ñервер не поддерживает локаль."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Ðевозможно уÑтановить модификаторы локали Ð´Ð»Ñ X Ñервера."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Ðевозможно найти правильный файл наÑтройки, иÑпользуетÑÑ Ð¿Ñ€Ð¾Ñтой по "
+"умолчанию."
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Ðевозможно загрузить тему."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"При обработке файлов конфигурации Openbox найдена одна или более "
+"ÑинтакÑичеÑких ошибок XML. Подробную информацию проÑмотрите в выводе "
+"stdout. ПоÑледнÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° замечена в файле \"%s\" Ñтроке %d, Ñ Ñообщением: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Ошибка ÑинтакÑиÑа Openbox"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "При перезапуÑке не удалоÑÑŒ выполнить новую команду \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "СинтакÑиÑ: openbox [options]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Опции:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Показать Ñту Ñправку и выйти\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Показать верÑию и выйти\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Заменить текущий запущенный менеджер окон\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file FILE Указать путь к иÑпользуемому файлу наÑтройки\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Разорвать Ñоединение Ñ Ð¼ÐµÐ½ÐµÐ´Ð¶ÐµÑ€Ð¾Ð¼ ÑеÑÑии\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Отправка Ñообщений запущенному ÑкземплÑру Openbox:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Перезагрузить конфигурацию Openbox\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart ПерезапуÑтить Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Выйти из Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"ÐаÑтройки отладки:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync ЗапуÑтить в режиме Ñинхронизации\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Показать вывод отладки\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus Показать вывод отладки Ð´Ð»Ñ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð½Ð¾Ð³Ð¾ фокуÑом\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Разделить диÑплей на фальшивые Ñкраны xinerama\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"ПожалуйÑта, Ñообщайте об ошибках на %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s требует ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð°\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Ðеверный аргумент командной Ñтроки \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Оконный менеджер уже запущен на Ñкране %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Ðевозможно получить выбранный менеджер окон на Ñкране %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Менеджер окон на Ñкране %d еще запущен"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, fuzzy, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox Ñконфигурирован Ð´Ð»Ñ %d рабочих Ñтолов, а в текущей ÑеÑÑии имеетÑÑ "
+"%d. Изменены наÑтройки Openbox."
+msgstr[1] ""
+"Openbox Ñконфигурирован Ð´Ð»Ñ %d рабочих Ñтолов, а в текущей ÑеÑÑии имеетÑÑ "
+"%d. Изменены наÑтройки Openbox."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "рабочий Ñтол %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "ЗапуÑк %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Ðеверный модификатор \"%s\" в комбинации клавиши/мыши"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Ðеверный код ключа \"%s\" в комбинации клавиш"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Ðеверное Ð¸Ð¼Ñ ÐºÐ»ÑŽÑ‡Ð° \"%s\" в комбинации клавиш"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Запрошенного ключа \"%s\" на Ñкране не ÑущеÑтвует"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Выйти из Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file требует ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð°\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "ДейÑтвие 'SessionLogout' недоÑтупно так как Openbox был Ñобран без "
+#~ "поддержки ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑеÑÑиÑми"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Ðевозможно Ñохранить ÑеÑÑию в \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Ошибка при Ñохранении ÑеÑÑии в \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Ðе подключен к менеджеру ÑеÑÑии"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Ошибка X: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Ðе удалоÑÑŒ запуÑтить \"%s\": %s"
diff --git a/po/sk.po b/po/sk.po
new file mode 100644
index 0000000..c126c8e
--- /dev/null
+++ b/po/sk.po
@@ -0,0 +1,487 @@
+# Translation of openbox.pot to Slovak.
+# Copyright (C) 2006 Mikael Magnusson
+# This file is distributed under the same license as the Openbox 3 package.
+# Jozef Říha <jose1711@gmail.com>, 2006, 2007.
+# František Eliáš <elias.frantisek@gmail.com>, 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox-3.4.8\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2009-07-16 17:30+0200\n"
+"Last-Translator: Frantisek Elias <elias.frantisek@gmail.com>\n"
+"Language-Team: Slovak <sk@sk.org>\n"
+"Language: sk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Vyžiadaná neplatná akcia \"%s\". Takáto akcia neexistuje."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Nie"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Ãno"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Spustiť"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Nepodarilo sa skonvertovať cestu \"%s\" z utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Zrušiť"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "UkonÄiÅ¥"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Naozaj sa chcete odhlásiť?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Odhlásiť sa"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "UrÄite chcete ukonÄiÅ¥ Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "UkonÄiÅ¥ Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Nepomenované okno"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "UkonÄujem proces..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Neodpovedá"
+
+#: openbox/client.c:3539
+#, 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 ""
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "UkonÄiÅ¥ proces"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr "Zdá sa, že okno \"%s\" neodpovedá. Chcete ho odpojiť z X serveru?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Odpojiť"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Prejsť na..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Správa plôch"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Pridať novú plochu"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Odstrániť poslednú plochu"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Okná"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Plochy"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "VÅ¡etky plochy"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Vrstva"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Vždy _navrchu"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "Nor_málna"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Vždy _dole"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "_Poslať na plochu"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Menu klienta"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "_Obnoviť"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "Pre_sunúť"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Z_mena veľkosti"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Do iko_ny"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximalizovať"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "Ro/_Zvinúť"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "(Ne)_Dekorovať"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "Z_avrieť"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Neplatný kontext \"%s\" v priradení myši"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Neplatné tlaÄidlo \"%s\" Å¡pecifikované v konfiguraÄnom súbore"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Nebolo možné vytvoriť adresár '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Zavrieť"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Konflikt priradenie klávesov v konfiguraÄnom súbore"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Nepodarilo sa nájsť platný súbor menu \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Nepodarilo sa spustiť príkaz pre pipe-menu \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Neplatný výstup z pipe-menu \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Pokus o sprístupnenie menu \"%s\", ale to neexistuje"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Viac..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Neplatné tlaÄidlo \"%s\" v priradení myÅ¡i"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Nepodarilo sa prejsť do domovského adresára \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Nepodarilo sa otvoriť displej z premennej prostredia DISPLAY"
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Nepodarilo sa inicializovať knižnicu obrender."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X server nepodporuje locale."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Nemôžem nastaviť locale pre X server."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Nepodarilo sa nájsÅ¥ platný konfiguraÄný súbor, použijem jednoduché "
+"implicitné nastavenia"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Nepodarilo sa nahrať tému."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr ""
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Reštart zlyhal pri spúšťaní novej binárky \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Syntax: openbox [volby]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Volby:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Zobrazi tuto napovedu a skonci\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Zobrazi cislo verzie a skonci\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr ""
+" --replace Nahradi momentalne beziace sedenie window manazera\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Vypne spojenie k manazerovi sedenia\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Predavanie sprav beziacej instancii Openboxu:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Opatovne nacita konfiguraciu Openboxu\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Restartuje Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr ""
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Volby ladenia:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Spustit v synchronnom mode\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Zobrazit ladiaci vystup\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Zobrazit ladiaci vystup pre manipulaciu s fokusom\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Rozdelit displej na neprave obrazovky xineramy\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Prosim hlaste chyby na %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s vyzaduje parameter\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Neplatny parameter prikazoveho riadku \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Okenny manazer uz bezi na obrazovke %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Nepodarilo sa získať výber okenného manažéra na obrazovke %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Okenný manažér na obrazovke %d sa neukonÄuje"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+msgstr[1] ""
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "plocha %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Spúšťam %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Neplatný kláves pre modifikátor \"%s\" v priradení klávesov/myši"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Neplatný kód klávesu \"%s\" v priradení klávesov"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Neplatný názov klávesu \"%s\" v priradení klávesov"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Požadovaný kláves \"%s\" na displeji neexistuje"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "UkonÄiÅ¥ Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file vyzaduje parameter\n"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Nepodarilo sa uložiť sedenie \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Chyba pri ukladaní sedenia do \"%s\": %s"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Chyba X: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Nepodarilo sa spustiť \"%s\": %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "Neplatné použitie akcie \"%s\". Akcia bude ignorovaná."
diff --git a/po/sr.po b/po/sr.po
new file mode 100644
index 0000000..f20be6d
--- /dev/null
+++ b/po/sr.po
@@ -0,0 +1,508 @@
+# Serbian translations for Openbox package
+# Copyright (C) 2008 Dana Jansens
+# This file is distributed under the same license as the Openbox 3 package.
+# Jay A. Fleming <tito.nehru.naser@gmail.com>, 2008.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2008-11-11 14:52+0100\n"
+"Last-Translator: Jay A. Fleming <tito.nehru.naser@gmail.com>\n"
+"Language-Team: None\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Захтевана је непоÑтојећа акција „%s“."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Ðе"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Да"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Изврши"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Претварање путање „%s“ из УТФ-8 није уÑпело"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Поништи"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Излаз"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "ЗаиÑта желите да Ñе одјавите?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Одјављивање"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "ЗаиÑта желите да изађете из ОпенбокÑа?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Излаз из ОпенбокÑа"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Безимени прозор"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Убијање..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Програм Ñе не одазива"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Изгледа да Ñе прозор „%s“ не одазива. Желите ли да га приморате на излаз "
+"Ñлањем Ñигнала %s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Завршетак процеÑа"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"Изгледа да Ñе прозор „%s“ не одазива. Желите ли да га одÑпојите од графичког "
+"Ñервера?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Прекид везе"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Иди овде..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Управљање радним површинама"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "Додајте нову радну површину"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "Уклоните поÑледњу радну површину"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Прозори"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Радне површине"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Све радне површине"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "Слој"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Увек изнад оÑталих"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Ðомално"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Увек на дну"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "ПремеÑти на радну површину"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "КориÑнички мени"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "Врати"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "Помери"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Промени величину"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Умањи"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Увећај"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "Замотај/Одмотај прозор"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Ðе/УкраÑи"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "Затвори"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Погрешан Ñадржај „%s“ у Ñпајању миша"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Погрешно дугме „%s“ наведено у датотеци за подешавање"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Ðе могу да направим директоријум '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Затвори"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Сукоб у комбинацији таÑтера у датотеци за подешавање"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Датотека за подешавање менија („%s“) није пронађена"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Ðије Ñе могла извршити команда за цевни-мени „%s“: %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Погрешан излаз из цевног-менија „%s“"
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Покушај приÑтупа менију „%s“ није уÑпео јер он не поÑтоји"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Више..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Погрешан таÑтер „%s“ у Ñпајању миша"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Ðе могу Ñе премеÑтити у Личну фаÑциклу „%s“:%s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Ðије уÑпео приÑтуп екрану из променљиве окружења „DISPLAY“"
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Ðије уÑпела иницијализација „obrender“ библиотеке."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "Графички Ñервер не подржава локалитет."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Ðе може Ñе поÑтавити измењивач локалитета за графички Ñервер"
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Ðе могу наћи иÑправне датотеке подешавања. КориÑтиће Ñе Ñамо оÑновна "
+"подешавања."
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Ðе могу учитати тему."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Код обраде датотека за подешавање ОпенбокÑа пронађено је једна или више "
+"ÑинтакÑних грешака (XML). ПоÑледња је била у датотеци „%s“, у линији %d, Ñа "
+"поруком: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "СинтакÑна грешка у ОпенбокÑу"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Поновно покретање није могло извршити нови програм „%s“: %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "ÐуторÑка права (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "СинтакÑа: openbox [опције]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Опције:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Прикажи ову помоћ и изађи\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Прикажи верзију и изађи\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Замени тренутно покренут управник прозора\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file FILE Ðаведите путању до датотеке Ñа подешавањима која ће Ñе "
+"кориÑтити\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Онемогући везу Ñа управљачем ÑеÑија\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"ПроÑлеђујем поруке покренутом примерку ОпенбокÑа:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Поново учитај подешавања за ОпенбокÑ\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Покрени опет ОпенбокÑ\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Изађи из ОпенбокÑа\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Опције отклањања грешака:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Изврши у иÑтовременом режиму\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Прикажи излаз код отклањања грешака\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Прикажи излаз код отклањања грешака за руковање "
+"фокуÑом\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Подели екран на имитације „xinerama“ екрана\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Пријавите грешке на %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s захтева одговарајући аргумент\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "ÐеиÑправан аргумент командне линије „%s“\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Управвник прозора је већ покренут на екрану %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "ÐиÑам могао да добијем избор управника прозора на екрану %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Управвник прозора на екрану %d није завршио Ñа радом"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ñ˜Ðµ подешен за %d радну површину, а тренутна ÑеÑија их има %d. "
+"Преклапање ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ð¿Ð¾Ð´ÐµÑˆÐ°Ð²Ð°ÑšÐ°."
+msgstr[1] ""
+"ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ñ˜Ðµ подешен за %d радне површине, а тренутна ÑеÑија их има %d. "
+"Преклапање ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ð¿Ð¾Ð´ÐµÑˆÐ°Ð²Ð°ÑšÐ°."
+msgstr[2] ""
+"ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ñ˜Ðµ подешен за %d радних површина, а тренутна ÑеÑија их има %d. "
+"Преклапање ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ð¿Ð¾Ð´ÐµÑˆÐ°Ð²Ð°ÑšÐ°."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "радна површина %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Извршавам %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "ÐеиÑправан измењивач таÑтера „%s“ у комбинацији таÑтер/миш"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "ÐеиÑправан код таÑтера „%s“ у комбинацији таÑтера"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "ÐеиÑправно име таÑтера „%s“ у комбинацији таÑтера"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Захтевани таÑтер „%s“ не поÑтоји на екрану"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "У реду"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Излаз из ОпенбокÑа"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file захтева одговарајући аргумент\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "Ðкција „SessionLogout“ није доÑтупна јер је ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ð¿Ñ€ÐµÐ²ÐµÐ´ÐµÐ½ без подршке "
+#~ "за управљање ÑеÑијама"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Ðе могу Ñачувати ÑеÑију у „%s“: %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Грешка приликом упиÑа у датотеку ÑеÑије „%s“: %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Ðије повезан Ñа управником ÑеÑија"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Грешка графичког Ñервера: %s"
diff --git a/po/sr@latin.po b/po/sr@latin.po
new file mode 100644
index 0000000..9997185
--- /dev/null
+++ b/po/sr@latin.po
@@ -0,0 +1,511 @@
+# Serbian translations for Openbox package
+# Copyright (C) 2008 Dana Jansens
+# This file is distributed under the same license as the Openbox 3 package.
+# Jay A. Fleming <tito.nehru.naser@gmail.com>, 2008.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2008-11-11 14:52+0100\n"
+"Last-Translator: Jay A. Fleming <tito.nehru.naser@gmail.com>\n"
+"Language-Team: None\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Zahtevana je nepostojeća akcija „%s“."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Ne"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Da"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Izvrši"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Pretvaranje putanje „%s“ iz UTF-8 nije uspelo"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Poništi"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Izlaz"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Zaista želite da se odjavite?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Odjavljivanje"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Zaista želite da izađete iz Openboksa?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Izlaz iz Openboksa"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Bezimeni prozor"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Ubijanje..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Program se ne odaziva"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Izgleda da se prozor „%s“ ne odaziva. Želite li da ga primorate na izlaz "
+"slanjem signala %s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Završetak procesa"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"Izgleda da se prozor „%s“ ne odaziva. Želite li da ga odspojite od grafiÄkog "
+"servera?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Prekid veze"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Idi ovde..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Upravljanje radnim površinama"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "Dodajte novu radnu površinu"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "Uklonite poslednju radnu površinu"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Prozori"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Radne površine"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Sve radne površine"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "Sloj"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Uvek iznad ostalih"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Nomalno"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Uvek na dnu"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "Premesti na radnu površinu"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "KorisniÄki meni"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "Vrati"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "Pomeri"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Promeni veliÄinu"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Umanji"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Uvećaj"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "Zamotaj/Odmotaj prozor"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Ne/Ukrasi"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "Zatvori"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Pogrešan sadržaj „%s“ u spajanju miša"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Pogrešno dugme „%s“ navedeno u datoteci za podešavanje"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Ne mogu da napravim direktorijum '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Zatvori"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Sukob u kombinaciji tastera u datoteci za podešavanje"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Datoteka za podešavanje menija („%s“) nije pronađena"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Nije se mogla izvršiti komanda za cevni-meni „%s“: %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Pogrešan izlaz iz cevnog-menija „%s“"
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Pokušaj pristupa meniju „%s“ nije uspeo jer on ne postoji"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Više..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Pogrešan taster „%s“ u spajanju miša"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Ne mogu se premestiti u LiÄnu fasciklu „%s“:%s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Nije uspeo pristup ekranu iz promenljive okruženja „DISPLAY“"
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Nije uspela inicijalizacija „obrender“ biblioteke."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "GrafiÄki server ne podržava lokalitet."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Ne može se postaviti izmenjivaÄ lokaliteta za grafiÄki server"
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Ne mogu naći ispravne datoteke podešavanja. Koristiće se samo osnovna "
+"podešavanja."
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Ne mogu uÄitati temu."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Kod obrade datoteka za podešavanje Openboksa pronađeno je jedna ili više "
+"sintaksnih grešaka (XML). Poslednja je bila u datoteci „%s“, u liniji %d, "
+"sa porukom: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Sintaksna greška u Openboksu"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Ponovno pokretanje nije moglo izvršiti novi program „%s“: %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Autorska prava (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Sintaksa: openbox [opcije]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Opcije:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Prikaži ovu pomoć i izađi\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Prikaži verziju i izađi\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Zameni trenutno pokrenut upravnik prozora\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file FILE Navedite putanju do datoteke sa podešavanjima koja će "
+"se koristiti\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Onemogući vezu sa upravljaÄem sesija\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Prosleđujem poruke pokrenutom primerku Openboksa:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Ponovo uÄitaj podeÅ¡avanja za Openboks\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Pokreni opet Openboks\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Izađi iz Openboksa\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Opcije otklanjanja grešaka:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Izvrši u istovremenom režimu\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Prikaži izlaz kod otklanjanja grešaka\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Prikaži izlaz kod otklanjanja grešaka za rukovanje "
+"fokusom\n"
+
+#: openbox/openbox.c:547
+#, fuzzy
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+" --debug-session Prikaži izlaz kod otklanjanja grešaka za rukovanje "
+"sessionom\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Podeli ekran na imitacije „xinerama“ ekrana\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Prijavite greške na %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s zahteva odgovarajući argument\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Neispravan argument komandne linije „%s“\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Upravvnik prozora je već pokrenut na ekranu %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Nisam mogao da dobijem izbor upravnika prozora na ekranu %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Upravvnik prozora na ekranu %d nije završio sa radom"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openboks je podešen za %d radnu površinu, a trenutna sesija ih ima %d. "
+"Preklapanje Openboks podešavanja."
+msgstr[1] ""
+"Openboks je podešen za %d radne površine, a trenutna sesija ih ima %d. "
+"Preklapanje Openboks podešavanja."
+msgstr[2] ""
+"Openboks je podešen za %d radnih površina, a trenutna sesija ih ima %d. "
+"Preklapanje Openboks podešavanja."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "radna površina %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Izvršavam %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Neispravan izmenjivaÄ tastera „%s“ u kombinaciji taster/miÅ¡"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Neispravan kod tastera „%s“ u kombinaciji tastera"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Neispravno ime tastera „%s“ u kombinaciji tastera"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Zahtevani taster „%s“ ne postoji na ekranu"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "U redu"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Izlaz iz Openboksa"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file zahteva odgovarajući argument\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "Akcija „SessionLogout“ nije dostupna jer je Openboks preveden bez podrške "
+#~ "za upravljanje sesijama"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Ne mogu saÄuvati sesiju u „%s“: %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Greška prilikom upisa u datoteku sesije „%s“: %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Nije povezan sa upravnikom sesija"
+
+#~ msgid "X Error: %s"
+#~ msgstr "GreÅ¡ka grafiÄkog servera: %s"
diff --git a/po/sv.po b/po/sv.po
new file mode 100644
index 0000000..33952a4
--- /dev/null
+++ b/po/sv.po
@@ -0,0 +1,503 @@
+# Swedish messages for openbox
+# Copyright (C) 2007 Mikael Magnusson
+# This file is distributed under the same license as the openbox package.
+# Mikael Magnusson <mikachu@icculus.org>, 2007.
+#
+msgid ""
+msgstr ""
+"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: 2011-08-01 18:11+0100\n"
+"Last-Translator: Mikael Magnusson <mikachu@icculus.org>\n"
+"Language-Team: None\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Ogiltig action \"%s\" efterfrågades, men den finns inte."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Nej"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Ja"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Kör"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Lyckades inte konvertera sökvägen \"%s\" från utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Avbryt"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Avsluta"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Är du säker på att du vill logga ut?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Logga ut"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Är du säker på att du vill avsluta Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Avsluta Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Namnlöst fönster"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Dödar..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Svarar inte"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Fönstret \"%s\" verkar inte svara. Vill du tvinga det att avslutas genom "
+"att skicka signalen %s?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Avsluta process"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"Fönstret \"%s\" verkar inte svara. Vill du stänga dess anslutning till X-"
+"servern?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Stäng anslutning"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Gå dit..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Hantera skrivbord"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Lägg till nytt skrivbord"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Ta bort sista skrivbordet"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Fönster"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Skrivbord"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Alla skrivbord"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Lager"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Alltid ö_verst"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normal"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Alltid _underst"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "_Skicka till skrivbord"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Klientmeny"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "Åt_erställ"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Flytta"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Ändra s_torlek"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Mi_nimera"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Ma_ximera"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "_Rulla upp/ner"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "_Dekorationer"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "Stän_g"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Ogiltig kontext \"%s\" i musbindning"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Ogiltig knapp \"%s\" angiven i konfigurationsfilen"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+"Openbox kompilerades utan stöd för att ladda bilder via Imlib2. Ikoner i "
+"menyer kommer inte att laddas."
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Kunde inte skapa katalogen '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Stäng"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Konflikt med annan tangentbindning i konfigurationsfilen"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Kunde inte hitta en giltig menyfil \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Misslyckades att köra kommando för pipe-menyn \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Ogiltig utdata från pipe-menyn \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Försökte öppna menyn \"%s\", men den finns inte"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Mer..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Ogiltig knapp \"%s\" i musbindning"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Kunde inte gå till hemkatalogen \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Kunde inte öppna en display från miljövariabeln DISPLAY."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Kunde inte initialisera obrender-biblioteket."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X-servern stödjer inte lokalisering."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Kan inte sätta lokaliseringsmodifierare för X-servern."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Kunde inte hitta en giltig konfigurationsfil, använder enkla standardvärden"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Kunde inte ladda ett tema."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Ett eller flera fel påträffades medan konfigurationsfilerna för Openbox "
+"lästes in. Se stdout för mer information. Det sista felet var i filen \"%s"
+"\" rad %d, med meddelandet: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox syntaxfel"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Restart misslyckades att starta nytt program \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Copyright (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Syntax: openbox [alternativ]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Alternativ:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Visa den här hjälpen och avsluta\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Visa versionen och avsluta\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Ersätt den befintliga fönsterhanteraren\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file FIL Ange sökvägen till konfigurationsfil att använda\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Avaktivera anslutning till sessionshanteraren\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Skicka meddelanden till en exekverande instans av Openbox:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Ladda om Openbox konfiguration\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Starta om Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Avsluta Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Debug-alternativ:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Kör i synkroniserat läge\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr " --startup CMD Kör CMD efter uppstart\n"
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Visa debuginformation\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus Visa debuginformation för fokushantering\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr " --debug-session Visa debuginformation för sessionshantering\n"
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Dela skärmen i simulerade xinerama-skärmar\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Rapportera buggar till %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s kräver ett argument\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Ogiltigt kommandoradsargument \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "En fönsterhanterare körs redan på skärm %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Kunde inte erhålla fönsterhanterarmarkeringen på skärm %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Fönsterhanteraren på skärm %d avslutar inte"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox är inställt på %d skrivbord, men nuvarande session har %d. Använder "
+"sessionens inställning."
+msgstr[1] ""
+"Openbox är inställt på %d skrivbord, men nuvarande session har %d. Använder "
+"sessionens inställning."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "skrivbord %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Kör %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Ogiltig modifikationstangent \"%s\" i tangent-/musbindning"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Ogiltig tangentkod \"%s\" i tantentbindning"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Ogiltigt tangentnamn \"%s\" i tangentbindning"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Efterfrågad tangent \"%s\" finns inte på displayen"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "OK"
+
+#~ msgid "Openbox"
+#~ msgstr "Openbox"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "Kommandot SessionLogout är inte tillgängligt eftersom Openbox "
+#~ "kompilerades utan stöd för sessionshantering"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Kunde inte spara sessionen till \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Ett fel inträffade när sessionen skulle sparas till \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Inte ansluten till en sessionshanterare"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X-fel: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Kunde inte exekvera \"%s\": %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "Ogiltigt användande av action \"%s\", den kommer ignoreras."
diff --git a/po/tr.po b/po/tr.po
new file mode 100644
index 0000000..0f03dec
--- /dev/null
+++ b/po/tr.po
@@ -0,0 +1,503 @@
+# Turkish translation of openbox.
+# Copyright (C) 2008 Dana Jansens
+# This file is distributed under the same license as the openbox package.
+# Tutku Dalmaz <mektup@tutkudalmaz.org>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2008-05-24 15:08+0300\n"
+"Last-Translator: Tutku Dalmaz <mektup@tutkudalmaz.org>\n"
+"Language-Team: Turkish\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "\"%s\" geçersiz eylem isteği. Böyle bir eylem yok."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Hayır"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Evet"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Çalıştır"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "\"%s\" yolu utf8'e çevrilmesi başarısız oldu"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Ä°ptal"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Çık"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Oturumu kapatmak istediÄŸinizden emin misiniz?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Oturumu Kapat"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Openbox'tan çıkmak istediğinize emin misiniz?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Openbox'tan Çık"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Ä°simsiz Pencere"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Sonlandırılıyor..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Cevap Vermiyor"
+
+#: openbox/client.c:3539
+#, 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 ""
+"\"%s\" penceresi cevap veriyor gibi görünmüyor. %s sinyali göndererek zorla "
+"sonlandırmak ister misiniz?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Süreci Sonlandır"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"\"%s\" penceresi cevap veriyor gibi görünmüyor. X sunucusu ile bağlantısını "
+"sonlandırmak ister misiniz?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Bağlantıyı Kes"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Oraya git..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Masaüstlerini yönet"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Yeni masaüstü ekle"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_Son masaüstünü kaldır"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Pencereler"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Masaüstleri"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Tüm masaüstleri"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "_Katman"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "_Her zaman üstte"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Normal"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "_Her zaman altta"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "_Masaüstüne gönder"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "İstemci menüsü"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "_Eski durumuna getir"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "_Taşı"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "_Yeniden boyutlandır"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "_Simge durumuna küçült"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "_Ekranı kapla"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "_Dürele/Aç"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Geri Al/Kapla"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "_Kapat"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Fare bağında geçersinz \"%s\" içeriği"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Yapılandırılma dosyasında belirtilmiş geçersiz \"%s\" düğmesi"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "'%s': %s dizini oluşturulamadı"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Kapat"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Yapılandırma dosyasındaki tuş bağlantısında çakışma"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "\"%s\" geçerli menü dosyası bulunamadı"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "İletim menüsü için \"%s\": %s komutunun çalıştırılması başarısız oldu"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "\"%s\" iletim menüsü için geçersiz çıkış"
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "\"%s\" menüsüne erişilmeye çalışıldı fakat bu menü yok"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Daha..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Fare bağında geçersiz \"%s\" tuşu"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "\"%s\": %s ev dizini deÄŸiÅŸtirilemedi"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "DISPLAY çevre değişkeninde görüntünün açılması başarısız oldu."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "ObRender kitaplığının sıfırlanması başarısız oldu."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X sunucusu dil ayarlarını desteklemiyor."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "X sunucu için dil ayarları değiştiricisi ayarlanamadı."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Geçerli yapılandırma dosyası bulunamadı, bazı basit öntanımlı ayarlar "
+"kullanılıyor"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Tema yüklenemedi."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Openbox yapılandırma dosyası ayrıştırılırken bir yada daha fazla XML "
+"sözdizimi hatası bulundu. Daha fazla bilgi için stdout çıktısına bakınız. "
+"Son hata \"%s\" dosyası içerisindeki %d satırında %s hata iletisi ile görüldü"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox Sözdizimi Hatası"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr ""
+"Yeniden başlatmadaki \"%s\": %s çalıştırılabilir dosyalarının başlatılması "
+"başarısız oldu"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Telif Hakkı (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Sözdizimi: openbox [seçenekler]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Seçenekler:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Yardımı görüntüle ve çık\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Sürüm bilgisini görüntüle ve çık\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Güncel pencere yöneticisini değiştir\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr ""
+" --config-file FILE Kullanılacak yapılandırma dosyasının yolunu belirtir\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr ""
+" --sm-disable Oturum yöneticisiyle olan bağlanıyı etkisiz kıl\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"İletiler çalışan bir Openbox örneğine aktarılıyor:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Openbox yapılandırmasını yeniden yükle\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Openbox'ı yeniden başlat\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Openbox'tan çık\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Hata ayıklama seçenekleri:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Eş zamanlı kipte çalış\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Hata ayıklama çıktısını göster\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Özelleşmiş durum için hata ayıklama çıktısını göster\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Görüntü gerçek olmayan ekranlara bölünür\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Lütfen hataları %s adresine bildiriniz\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s dosyası bir değişkene ihtiyaç duyuyor\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "\"%s\" geçersiz komut satırı değişkeni\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "%d ekranınz zaten bir pencere yöneticixi çalışıyor"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Seçilen %d ekranında pencere yöneticisi bulunamadı"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "%d ekranındaki pencere yöneticisinden çıkılamıyor"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, fuzzy, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox %d masaüstleri için yapılandırılmıştır fakat güncel oturum %d dir. "
+"Openbox yapılandırmasının üzerien yazılıyor."
+msgstr[1] ""
+"Openbox %d masaüstleri için yapılandırılmıştır fakat güncel oturum %d dir. "
+"Openbox yapılandırmasının üzerien yazılıyor."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "%i masaüstü"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "%s çalışıyor"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Klavye/fare bağında geçersiz \"%s\" tuş değiştirici"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Tuş bağında \"%s\" geçersiz tuş kodu"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Tuş bağında \"%s\" geçersiz tuş adı"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "İstenilen \"%s\" tuşu görüntüde yok"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "Tamam"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Openbox'tan Çık"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file dosyası bir değişkene ihtiyaç duyuyor\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "Openbox oturum yönetim desteği olmaksızın yapılandırıldığı için "
+#~ "SessionLogout eylemi geçerli değildir."
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "\"%s\": %s oturumu kaydedilemedi"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Oturum \"%s\": %s'e kaydedilirken hata"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Oturum yöneticisine bağlı değil"
+
+#~ msgid "X Error: %s"
+#~ msgstr "%s X Hatası"
diff --git a/po/uk.po b/po/uk.po
new file mode 100644
index 0000000..a108e37
--- /dev/null
+++ b/po/uk.po
@@ -0,0 +1,500 @@
+# Ukrainian translation for Openbox.
+# Copyright (C) 2007 Dmitriy Moroz
+# Copyright (C) 2008 Serhiy Lysovenko
+# This file is distributed under the same license as the openbox package.
+# Dmitriy Moroz <zux@dimaka.org.ua>, 2007.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.2\n"
+"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n"
+"POT-Creation-Date: 2011-08-01 18:20+0200\n"
+"PO-Revision-Date: 2008-12-09 20:12+0200\n"
+"Last-Translator: Serhiy Lysovenko <lisovenko.s[at]gmail[dot]com>\n"
+"Language-Team: Ukrainian <linux.org.ua>\n"
+"Language: uk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "ЗдійÑнено запит на неіÑнуючу дію \"%s\"."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "ÐÑ–"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Так"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Виконати"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Ðе вдалоÑÑ ÐºÐ¾Ð½Ð²ÐµÑ€Ñ‚ÑƒÐ²Ð°Ñ‚Ð¸ шлÑÑ… \"%s\" з utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "СкаÑувати"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Вихід"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Ви дійÑно бажаєте завершити ÑеанÑ?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Вийти"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Ви дійÑно хочете вийти з Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Вийти з Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Ðеназване вікно"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "ЗнищеннÑ..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Ðе відповідає"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Схоже, вікно \"%s\" не відповідає. Чи бажаєте примуÑово завершити програму, "
+"поÑлавши Ñигнал \"%s\"?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "ПримуÑове завершеннÑ"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr "Вікно \"%s\" не відповідає. Чи бажаєте його від'єднати від X Ñервера?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Від'єднати"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Перейти..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÑтільницÑми"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "Додати нову Ñтільницю (_A)"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "Видалити оÑтанню Ñтільницю (_R)"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Вікна"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Стільниці"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Ðа вÑÑ–Ñ… ÑтільницÑÑ…"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "Шар (_L)"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Ðад уÑіма вікнами (_T)"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "Звичайне Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð½Ñ (_N)"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Під вікнами (_B)"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "Відправити на Ñтільницю (_S)"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Меню клієнта"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "Відновити (_E)"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "ПереміÑтити (_M)"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Змінити розмір (_Z)"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Згорнути (_N)"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Розгорнути (_X)"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "Скрутити/Розкрутити (_R)"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "Перемкнути декорацію (_D)"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "Закрити (_C)"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Ðекоректний контекÑÑ‚ \"%s\" в прив'Ñзці клавіш мишки"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Ðекоректна кнопка \"%s\" вказана у файлі конфігурації"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Ðе вдалоÑÑ Ñтворити каталог '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Закрити"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Конфлікт прив'Ñзки клавіш у файлі конфігурації"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ коректний файл меню \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ команду Ð´Ð»Ñ pipe-меню \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Ðекоректний вивід з pipe-меню \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Спроба доÑтупу до неіÑнуючого меню \"%s\""
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Більше..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Ðекоректна клавіша \"%s\" в прив'Ñзці клавіш мишки"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ¹Ñ‚Ð¸ до домашнього каталогу \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ диÑплей зі змінної Ñередовища DISPLAY"
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Ðе вдалоÑÑ Ñ–Ð½Ñ–Ñ†Ð°Ñ–Ð»Ñ–Ð·ÑƒÐ²Ð°Ñ‚Ð¸ бібліотеку obrender"
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X-Ñервер не підтримує локалі"
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Ðе можу вÑтановити модифікатори локалі Ð´Ð»Ñ X-Ñервера"
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr ""
+"Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ коректний файл конфігурації, викориÑтовую Ñтандартні "
+"налаштуваннÑ"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ тему"
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"ВиÑвлено одну або більше ÑинтакÑичних помилок XML під Ñ‡Ð°Ñ ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ "
+"конфігураційних файлів Openbox. Щоб дізнатиÑÑŒ більше - переглÑньте stdout. "
+"ОÑÑ‚Ð°Ð½Ð½Ñ Ð¿Ð¾Ð¼Ñ–Ñ‡ÐµÐ½Ð° помилка була в файлі \"%s\", Ñтрічка %d, повідомленнÑ: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "ÑинтакÑична помилка Openbox"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr ""
+"При перезавантаженні не вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ новий виконуваний файл \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "ÐвторÑькі права (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "СинтакÑ: openbox [параметри]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Параметри:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Показати цю довідку і вийти\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --vesrion Показати верÑÑ–ÑŽ Ñ– вийти\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace Замінити запущений менеджер вікон\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file ФÐЙЛ Вказати шлÑÑ… до конфігураційного файлу\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Вимкнути з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· менеджером ÑеанÑу\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"Передача повідомлень процеÑу Openbox, що виконуєтьÑÑ\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Перезавантажити конфігурацію Openbox'у\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart ПерезапуÑтити Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Вийти з Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Ðалагоджувальні параметри\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync ЗапуÑтити в Ñинхронному режимі\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Показувати інформацію налагоджуваннÑ\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus Показувати відлагоджувальний вивід Ð´Ð»Ñ ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ "
+"фокуÑом\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Розбити екран на фальшиві екрани xinerama\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Будь-лаÑка, повідомлÑйте про помилки на %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s потребує аргументу\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Ðекоректний аргумент \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Ðа диÑплеї %d вже запущений менеджер вікон"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Ðе можу запуÑтити менеджера вікон на диÑплеї %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Менеджер вікон на диÑплеї %d не завершаєтьÑÑ"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, fuzzy, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox Ñконфігуровано на %d диÑплеїв, але в поточній ÑеÑÑ–Ñ— викориÑтовуєтьÑÑ "
+"%d. ÐŸÐµÑ€ÐµÐ²Ð¸Ñ‰ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ— Openbox."
+msgstr[1] ""
+"Openbox Ñконфігуровано на %d диÑплеїв, але в поточній ÑеÑÑ–Ñ— викориÑтовуєтьÑÑ "
+"%d. ÐŸÐµÑ€ÐµÐ²Ð¸Ñ‰ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ— Openbox."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "ÑÑ‚Ñ–Ð»ÑŒÐ½Ð¸Ñ†Ñ %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "ВиконуєтьÑÑ %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr ""
+"Ðекоректна назва модифікатору \"%s\" у прив'Ñзці клавіш клавіатури/мишки"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Ðекоректний код клавіші \"%s\" у прив'Ñзці клавіш"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Ðекоректна назва клавіші \"%s\" у прив'Ñзці клавіш"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Потрібної кнопки \"%s\" нема на екрані"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "Гаразд"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Вийти з Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file потребує аргументу\n"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ ÑеÑÑ–ÑŽ в \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "Помилка при збереженні ÑеÑÑ–Ñ— в \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Ðе під'єднано до керівника ÑеÑÑ–Ñми"
+
+#~ msgid "X Error: %s"
+#~ msgstr "Помилка X-Ñерверу: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "ÐевдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ \"%s\": %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "Ðекоректне викриÑÑ‚Ð°Ð½Ð½Ñ Ð´Ñ–Ñ— \"%s\". Ð”Ñ–Ñ Ð±ÑƒÐ´Ðµ проігнорована."
diff --git a/po/vi.po b/po/vi.po
new file mode 100644
index 0000000..d881b6d
--- /dev/null
+++ b/po/vi.po
@@ -0,0 +1,503 @@
+# Vietnamese messages for Openbox.
+# Copyright (C) 2007 Dana Jansens
+# This file is distributed under the same license as the Openbox package.
+# Quan Tran <qeed.quan@gmail.com>, 2007.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7\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-11 02:07+0100\n"
+"Last-Translator: Quan Tran <qeed.quan@gmail.com>\n"
+"Language-Team: None\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "Hành động \"%s\" làm không được. Hành động đó không có."
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "Không"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "Äược"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "Hành động"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "Không thể chuyển chỗ \"%s\" từ utf8"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "Bãi bá»"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "Äi ra"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "Có chắc chắn đi ra không?"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "Äi ra"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "Có chắc chắn đi ra Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "Äi ra Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "Cửa sổ không tên"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "Äang giết..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "Không phản ứng"
+
+#: openbox/client.c:3539
+#, 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 ""
+"Cái cửa sổ \"%s\" không phản ứng được. Có muốn bắt nó đi ra bằng gửi đi %s "
+"tính hiệu?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "Giết Process"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr ""
+"Cái cá»­a sổ \"%s\" không phản ứng được. Có muốn rá»i nó ra X server không"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "Rá»i ra"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "Äi đến chá»— đó"
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "Quản lý chỗ làm việc"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "_Cộng thêm chỗ làm việc"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "_BỠlát chỗ làm việc"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "Cửa sổ"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "Chỗ làm việc"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "Tất cả chỗ làm việc"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "Lá»›_p"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "Luôn luôn ở _trên"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "_Bình thÆ°á»ng"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "Luôn luôn ở _dưới"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "Gửi đến chỗ làm _việc"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "Khách thực đơn"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "_Hoàn lại"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "Chu_yển đi"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "Làm _nhỠhơn/lớn hơn"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "Biến _xuống"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "Biến _lớn nhất"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "_Cuốn lên/xuống"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "_Trang/Không Trang trí"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "Äón_g"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "Vô hiệu văn cảnh \"%s\" ở trong chuột đặt"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "Sai nút \"%s\" ở trong hình thể"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "Không thể chế directory '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "Äóng"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "Xung đột với chữ trói ở trong hình thể"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "Không có thể tìm vững chắc thực đơn \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "Không có thể chạy lệnh cho ống-thực đơn \"%s\": %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "Vô hiệu sản xuất của ống-thực đơn \"%s\""
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "Thử mở thực đơn \"%s\" nhưng mà cái đó không có"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "Thêm nữa"
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "Vô hiệu nút \"%s\" ở trong máy chuột đặt"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "Không thể đổi đến chỗ nhà \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "Không mở hình từ DISPLAY được."
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "Không mở được thư viện obrender."
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "Chương trình X không có locale cho tiếng nay."
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "Không thể dùng locale cho chương trình X."
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr "Không thể tìm ra hình thể, sẽ dùng bắt đầu hình thể"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "Không thể Ä‘á»c theme."
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"Một hay là trên một XML danh từ không đúng tìm thấy ở trong Openbox tài "
+"liệu. Coi stdout cho biết thêm. Cai sai lầm cuối cùng ở trong Openbox tài "
+"liệu \"%s\" ở hàng %d vá»›i lá»i: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox danh từ không đúng"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "Bắt đầu lại há»ng mở được executable má»›i \"%s\": %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "Bản quyá»n (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "Cách dùng: openbox [chá»n lá»±a]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"Chá»n lá»±a:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help Trưng bày giúp đỡ này và đi ra\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version Trưng bày số của chương trình và đi ra\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr ""
+" --replace Thay thế chương trình quản lý cửa sổ cho đến openbox\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file FILE Chỉ chá»— Ä‘Æ°á»ng cho tài liệu để dùng\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable Tắt liên lạc đến session quản lý\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"ÄÆ°a thông báo cho chÆ°Æ¡ng trình Openbox:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure Bắt đầu lại Openbox's tài liệu\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart Bắt đầu lại Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit Äi ra Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"Debugging chá»n lá»±a:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync Chạy trong cách thức synchronous\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug Trưng bày debugging đoàn chữ\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr ""
+" --debug-focus TrÆ°ng bày debugging Ä‘oàn chữ cho Ä‘iá»u khiển tập trung\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama Tách trưng bày vào giả xinerama màn\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"Làm ơn báo cáo bugs ở chỗ %s\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s cần chá»n lá»±a má»™t tài liệu\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "Mệnh lệnh viết sai \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "Chương trình quản lý cửa sổ khác đang chạy trên màn hình %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "Không thể lấy được chương trình quản lý cửa sổ ở trên màn hình %d"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "Chương trình quản lý cửa sổ trên màn hình %d không đi ra"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, fuzzy, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox đặt cho %d chỗ làm việc, nhưng mà session hiện đại có %d. Lật đổ "
+"openbox tài liệu cho cái mới."
+msgstr[1] ""
+"Openbox đặt cho %d chỗ làm việc, nhưng mà session hiện đại có %d. Lật đổ "
+"openbox tài liệu cho cái mới."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "chỗ làm việc %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "Äan Chạy %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "Vô hiệu Modifier key \"%s\" ở chỗ máy keyboard/chuột đặt"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "Vô hiệu key mã \"%s\" ở chỗ key đặt"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "Vô hiệu key tên \"%s\" ở chỗ key đặt"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "Yêu cầu key \"%s\" không có ở chỗ màn hình"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "Äồng ý"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "Äi ra Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file cần chá»n lá»±a má»™t tài liệu\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "Không thể làm SessionLogout được bởi vì Openbox không bỠ\"session "
+#~ "management support\" khi compile nó"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "Không thể tiết kiệm thá»i kỳ cho \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "BÄ© trục chật lúc tiết kiệm thá»i kỳ cho \"%s\": %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "Không hàng với session quản lý"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X trục chật: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "Làm không được \"%s\": %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "Sự dùng hành động \"%s\" sai rồi. Không làm hành động đó."
diff --git a/po/zh_CN.po b/po/zh_CN.po
new file mode 100644
index 0000000..4f2761a
--- /dev/null
+++ b/po/zh_CN.po
@@ -0,0 +1,495 @@
+# Simplified Chinese Messages for openbox.
+# Copyright (C) 2007 Mikael Magnusson
+# This file is distributed under the same license as the openbox package.
+#
+# Xiaoyu PENG <peng.xiaoyu@gmail.com>, 2007.
+# Shaodong Di <gnuyhlfh@gmail.com>, 2008.
+# zhou sf <sxzzsf@gmail.com>, 2008.
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7\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-11 22:07+0800\n"
+"Last-Translator: zhou sf <sxzzsf@gmail.com>\n"
+"Language-Team: Simplified Chinese\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "请求的动作 \"%s\" 无效。该动作ä¸å­˜åœ¨ã€‚"
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "å¦"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "是"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "执行"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "从 utf8 转æ¢è·¯å¾„ \"%s\" 时失败"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "å–消"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "退出"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "确认注销�"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "注销"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "确认退出 Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "退出 Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "未命å窗å£"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "æ€æ­»ä¸­..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "æ— å“应"
+
+#: openbox/client.c:3539
+#, 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 "çª—å£ \"%s\" 似乎失去了å“应. å‘é€ä¿¡å· %s 以强制退出å—?"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "结æŸè¿›ç¨‹"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr "çª—å£ \"%s\" 似乎失去了å“应. 断开其与 X æœåŠ¡å™¨çš„连接?"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "断开连接"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "跳转到..."
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "管ç†æ¡Œé¢"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "添加新桌é¢(_A)"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "移除最åŽä¸€ä¸ªæ¡Œé¢(_R)"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "窗å£"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "æ¡Œé¢"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "所有桌é¢"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "层(_L)"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "总在最上层(_T)"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "常规(_N)"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "总在最底层(_B)"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "å‘é€åˆ°æ¡Œé¢(_S)"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "客户端èœå•"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "还原(_E)"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "移动(_M)"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "调整大å°(_Z)"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "最å°åŒ–(_N)"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "最大化(_X)"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "å·èµ·/放下(_R)"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "去除装饰(_D)"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "关闭(_C)"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "鼠标绑定中无效的上下文 \"%s\""
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "é…置文件中指定的按钮 \"%s\" 无效"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "无法创建目录 '%s': %s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "关闭"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "é…置文件中的组åˆé”®å†²çª"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "无法找到有效的èœå•æ–‡ä»¶ \"%s\""
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "执行管é“èœå•çš„命令 \"%s\" 时失败: %s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "管é“èœå• \"%s\" 的输出无效"
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "å°è¯•è¯»å–èœå• \"%s\",但是它ä¸å­˜åœ¨"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "更多..."
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "鼠标绑定中的无效按键 \"%s\""
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "无法切æ¢åˆ°ä¸»ç›®å½• \"%s\": %s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "在打开DISPLAY环境å˜é‡æ‰€æŒ‡å®šçš„X显示时失败。"
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "åˆå§‹åŒ–obrender库时失败。"
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "XæœåŠ¡å™¨ä¸æ”¯æŒlocale。"
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "无法设置XæœåŠ¡å™¨çš„locale修饰键。"
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr "无法找到有效的é…置文件,使用一些简å•çš„默认值"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "无法读入主题。"
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"å½“è§£æž Openbox é…置文件时å‘现一个或多个 XML 语法错误. 更多信æ¯æŸ¥çœ‹ stdout. "
+"最近的错误出现于文件 \"%s\" 中第 %d 行的: %s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox 语法错误"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "é‡æ–°å¯åŠ¨ä»¥æ‰§è¡Œæ–°çš„å¯æ‰§è¡Œæ–‡ä»¶ \"%s\" 时失败: %s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "版æƒæ‰€æœ‰ (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "用法: openbox [选项]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"选项: \n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help 显示该帮助信æ¯åŽé€€å‡º\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version 显示版本å·åŽé€€å‡º\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace 替æ¢å½“å‰è¿è¡Œçš„窗å£ç®¡ç†å™¨\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file FILE 使用指定的é…置文件\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable ç¦æ­¢è¿žæŽ¥åˆ°ä¼šè¯ç®¡ç†å™¨\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"传递信æ¯ç»™è¿è¡Œä¸­çš„ Openbox 实例:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure é‡æ–°è½½å…¥ Openbox çš„é…ç½®\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart é‡æ–°å¯åŠ¨ Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit 退出 Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"调试选项:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync 在åŒæ­¥æ¨¡å¼ä¸­è¿è¡Œ\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug 显示调试输出\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus 显示焦点处ç†çš„调试输出\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama 分割显示到伪造的 xinerama å±å¹•ä¸­\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"è¯·å‘ %s 报告错误\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s 需è¦ä¸€ä¸ªå‚æ•°\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "无效的命令行å‚æ•° \"%s\"\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "å·²ç»æœ‰çª—å£ç®¡ç†å™¨è¿è¡Œåœ¨å±å¹• %d"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "在å±å¹• %d 无法被选为窗å£ç®¡ç†å™¨"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "å±å¹• %d 的窗å£ç®¡ç†å™¨æ²¡æœ‰é€€å‡º"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, fuzzy, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox é…置了 %d 个桌é¢, 当å‰ä¼šè¯æ‹¥æœ‰ %d æ¡Œé¢. 覆盖 Openbox çš„é…ç½®."
+msgstr[1] ""
+"Openbox é…置了 %d 个桌é¢, 当å‰ä¼šè¯æ‹¥æœ‰ %d æ¡Œé¢. 覆盖 Openbox çš„é…ç½®."
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "æ¡Œé¢ %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "è¿è¡Œ %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "键盘/鼠标的绑定 \"%s\" 无效"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "æŒ‰ç›˜ç»‘å®šä¸­æ— æ•ˆçš„é”®ç›˜ç  \"%s\""
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "按键绑定中无效的键å \"%s\""
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "请求的按键 \"%s\" 在显示中ä¸å­˜åœ¨"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "好"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "退出 Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file 需è¦ä¸€ä¸ªå‚æ•°\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr "因为编译 Openbox 时未支æŒä¼šè¯ç®¡ç†, å› æ­¤ SessionLogout 动作无效."
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "无法ä¿å­˜ä¼šè¯åˆ° \"%s\": %s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "在ä¿å­˜ä¼šè¯åˆ° \"%s\" 时出错: %s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "未连接到会è¯ç®¡ç†å™¨"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X 错误: %s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "执行 \"%s\" 时失败: %s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "使用的动作 \"%s\" 无效。动作将被忽略。"
diff --git a/po/zh_TW.po b/po/zh_TW.po
new file mode 100644
index 0000000..d8bd94a
--- /dev/null
+++ b/po/zh_TW.po
@@ -0,0 +1,495 @@
+# Traditional Chinese Messages for openbox.
+# Copyright (C) 2006 Mikael Magnusson
+# This file is distributed under the same license as the openbox package.
+# Wei-Lun Chao <chaoweilun@gmail.com>, 2006, 07.
+# 洪任諭 <pcman.tw@gmail.com>, 2008
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Openbox 3.4.7\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-06 01:01+0800\n"
+"Last-Translator: 洪任諭 <pcman.tw@gmail.com>\n"
+"Language-Team: Chinese (traditional) <zh-l10n@linux.org.tw>\n"
+"Language: zh_TW\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: openbox/actions.c:198
+#, c-format
+msgid "Invalid action \"%s\" requested. No such action exists."
+msgstr "è¦æ±‚的動作「%sã€ç„¡æ•ˆã€‚無此類動作存在。"
+
+#: openbox/actions/execute.c:147
+msgid "No"
+msgstr "å¦"
+
+#: openbox/actions/execute.c:148
+msgid "Yes"
+msgstr "是"
+
+#: openbox/actions/execute.c:152
+msgid "Execute"
+msgstr "執行"
+
+#: openbox/actions/execute.c:161
+#, c-format
+msgid "Failed to convert the path \"%s\" from utf8"
+msgstr "轉æ›è·¯å¾‘「%sã€è‡ª utf8 時失敗"
+
+#: openbox/actions/exit.c:69 openbox/client.c:3550
+msgid "Cancel"
+msgstr "å–消"
+
+#: openbox/actions/exit.c:70
+msgid "Exit"
+msgstr "離開"
+
+#: openbox/actions/exit.c:74
+msgid "Are you sure you want to log out?"
+msgstr "你確定è¦ç™»å‡ºå—Žï¼Ÿ"
+
+#: openbox/actions/exit.c:75
+msgid "Log Out"
+msgstr "登出"
+
+#: openbox/actions/exit.c:78
+msgid "Are you sure you want to exit Openbox?"
+msgstr "你確定è¦é›¢é–‹ Openbox?"
+
+#: openbox/actions/exit.c:79
+msgid "Exit Openbox"
+msgstr "離開 Openbox"
+
+#: openbox/client.c:2037
+msgid "Unnamed Window"
+msgstr "未命å視窗"
+
+#: openbox/client.c:2051 openbox/client.c:2082
+msgid "Killing..."
+msgstr "正在中止..."
+
+#: openbox/client.c:2053 openbox/client.c:2084
+msgid "Not Responding"
+msgstr "沒有回應"
+
+#: openbox/client.c:3539
+#, 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 "視窗「%sã€ä¼¼ä¹Žå·²ç¶“åœæ­¢å›žæ‡‰ã€‚ 你想é€å‡º \"%s\" 訊æ¯å¼·åˆ¶çµæŸç¨‹å¼å—Žï¼Ÿ"
+
+#: openbox/client.c:3541
+msgid "End Process"
+msgstr "çµæŸ Process"
+
+#: openbox/client.c:3545
+#, c-format
+msgid ""
+"The window \"%s\" does not seem to be responding. Do you want to disconnect "
+"it from the X server?"
+msgstr "視窗「%sã€ä¼¼ä¹Žå·²ç¶“åœæ­¢å›žæ‡‰ã€‚ 你想從 X 伺æœå™¨å°‡å®ƒæ–·ç·šå—Žï¼Ÿ"
+
+#: openbox/client.c:3547
+msgid "Disconnect"
+msgstr "æ–·ç·š"
+
+#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90
+msgid "Go there..."
+msgstr "到那è£åŽ»â€¦"
+
+#: openbox/client_list_combined_menu.c:100
+msgid "Manage desktops"
+msgstr "管ç†æ¡Œé¢"
+
+#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166
+msgid "_Add new desktop"
+msgstr "加入新桌é¢(_A)"
+
+#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167
+msgid "_Remove last desktop"
+msgstr "移除尾端桌é¢(_R)"
+
+#: openbox/client_list_combined_menu.c:157
+msgid "Windows"
+msgstr "視窗"
+
+#: openbox/client_list_menu.c:214
+msgid "Desktops"
+msgstr "æ¡Œé¢"
+
+#: openbox/client_menu.c:259
+msgid "All desktops"
+msgstr "所有桌é¢"
+
+#: openbox/client_menu.c:371
+msgid "_Layer"
+msgstr "層次(_L)"
+
+#: openbox/client_menu.c:376
+msgid "Always on _top"
+msgstr "最上層(_T)"
+
+#: openbox/client_menu.c:377
+msgid "_Normal"
+msgstr "一般(_N)"
+
+#: openbox/client_menu.c:378
+msgid "Always on _bottom"
+msgstr "最下層(_B)"
+
+#: openbox/client_menu.c:380
+msgid "_Send to desktop"
+msgstr "傳é€åˆ°æ¡Œé¢(_S)"
+
+#: openbox/client_menu.c:384
+msgid "Client menu"
+msgstr "客戶端é¸å–®"
+
+#: openbox/client_menu.c:394
+msgid "R_estore"
+msgstr "還原(_E)"
+
+#: openbox/client_menu.c:398
+msgid "_Move"
+msgstr "移動(_M)"
+
+#: openbox/client_menu.c:400
+msgid "Resi_ze"
+msgstr "調整大å°(_Z)"
+
+#: openbox/client_menu.c:402
+msgid "Ico_nify"
+msgstr "最å°åŒ–(_N)"
+
+#: openbox/client_menu.c:406
+msgid "Ma_ximize"
+msgstr "最大化(_X)"
+
+#: openbox/client_menu.c:410
+msgid "_Roll up/down"
+msgstr "å‘上/å‘下æ²å‹•(_R)"
+
+#: openbox/client_menu.c:414
+msgid "Un/_Decorate"
+msgstr "é–‹/關視窗è£é£¾(_D)"
+
+#: openbox/client_menu.c:418
+msgid "_Close"
+msgstr "關閉(_C)"
+
+#: openbox/config.c:503
+#, c-format
+msgid "Invalid context \"%s\" in mouse binding"
+msgstr "與滑鼠組åˆçš„上下文「%sã€ç„¡æ•ˆ"
+
+#: openbox/config.c:857
+#, c-format
+msgid "Invalid button \"%s\" specified in config file"
+msgstr "在é…置檔中指定的按鈕「%sã€ç„¡æ•ˆ"
+
+#: openbox/config.c:882
+msgid ""
+"Openbox was compiled without Imlib2 image loading support. Icons in menus "
+"will not be loaded."
+msgstr ""
+
+#: openbox/debug.c:55
+#, c-format
+msgid "Unable to make directory '%s': %s"
+msgstr "無法製作目錄'%s':%s"
+
+#: openbox/debug.c:138 openbox/openbox.c:372
+msgid "Close"
+msgstr "關閉"
+
+#: openbox/keyboard.c:161
+msgid "Conflict with key binding in config file"
+msgstr "與é…置檔中的按éµçµ„åˆè¡çª"
+
+#: openbox/menu.c:94 openbox/menu.c:106
+#, c-format
+msgid "Unable to find a valid menu file \"%s\""
+msgstr "無法找到有效的é¸å–®æª”案「%sã€"
+
+#: openbox/menu.c:158
+#, c-format
+msgid "Failed to execute command for pipe-menu \"%s\": %s"
+msgstr "執行命令於管線é¸å–®ã€Œ%sã€æ™‚失敗:%s"
+
+#: openbox/menu.c:172
+#, c-format
+msgid "Invalid output from pipe-menu \"%s\""
+msgstr "從管線é¸å–®ã€Œ%sã€çš„輸出無效"
+
+#: openbox/menu.c:185
+#, c-format
+msgid "Attempted to access menu \"%s\" but it does not exist"
+msgstr "試圖存å–é¸å–®ã€Œ%sã€ä½†æ˜¯å®ƒä¸å­˜åœ¨"
+
+#: openbox/menu.c:400 openbox/menu.c:401
+msgid "More..."
+msgstr "更多…"
+
+#: openbox/mouse.c:376
+#, c-format
+msgid "Invalid button \"%s\" in mouse binding"
+msgstr "與滑鼠組åˆçš„按鈕「%sã€ç„¡æ•ˆ"
+
+#: openbox/openbox.c:137
+#, c-format
+msgid "Unable to change to home directory \"%s\": %s"
+msgstr "無法變更到主目錄「%sã€ï¼š%s"
+
+#: openbox/openbox.c:152
+msgid "Failed to open the display from the DISPLAY environment variable."
+msgstr "é–‹å•Ÿä¾ DISPLAY 環境變數所指的顯示時失敗。"
+
+#: openbox/openbox.c:182
+msgid "Failed to initialize the obrender library."
+msgstr "åˆå§‹åŒ– obrender 函å¼åº«æ™‚失敗。"
+
+#: openbox/openbox.c:193
+msgid "X server does not support locale."
+msgstr "X 伺æœå™¨ä¸æ”¯æ´èªžå€ã€‚"
+
+#: openbox/openbox.c:195
+msgid "Cannot set locale modifiers for the X server."
+msgstr "無法設定用於 X 伺æœå™¨çš„語å€ä¿®é£¾é …。"
+
+#: openbox/openbox.c:253
+msgid "Unable to find a valid config file, using some simple defaults"
+msgstr "無法找到有效的é…置檔案,而使用æŸäº›ç°¡å–®çš„é è¨­å€¼"
+
+#: openbox/openbox.c:286
+msgid "Unable to load a theme."
+msgstr "無法載入佈景主題。"
+
+#: openbox/openbox.c:370
+#, c-format
+msgid ""
+"One or more XML syntax errors were found while parsing the Openbox "
+"configuration files. See stdout for more information. The last error seen "
+"was in file \"%s\" line %d, with message: %s"
+msgstr ""
+"è§£æž Openbox 設定檔 XML 語法時發ç¾ä¸€å€‹æˆ–多個錯誤。 查看 stdout 以ç²å¾—更多資"
+"訊。 最後一個發ç¾çš„錯誤在檔案 \"%s\" 第 %d 行。訊æ¯ï¼š%s"
+
+#: openbox/openbox.c:372
+msgid "Openbox Syntax Error"
+msgstr "Openbox 語法錯誤"
+
+#: openbox/openbox.c:438
+#, c-format
+msgid "Restart failed to execute new executable \"%s\": %s"
+msgstr "é‡æ–°å•Ÿå‹•ä»¥åŸ·è¡Œæ–°çš„å¯åŸ·è¡Œæª”「%sã€æ™‚失敗:%s"
+
+#: openbox/openbox.c:517 openbox/openbox.c:519
+msgid "Copyright (c)"
+msgstr "著作權 (c)"
+
+#: openbox/openbox.c:528
+msgid "Syntax: openbox [options]\n"
+msgstr "語法:openbox [é¸é …]\n"
+
+#: openbox/openbox.c:529
+msgid ""
+"\n"
+"Options:\n"
+msgstr ""
+"\n"
+"é¸é …:\n"
+
+#: openbox/openbox.c:530
+msgid " --help Display this help and exit\n"
+msgstr " --help 顯示此說明然後離開\n"
+
+#: openbox/openbox.c:531
+msgid " --version Display the version and exit\n"
+msgstr " --version 顯示版本然後離開\n"
+
+#: openbox/openbox.c:532
+msgid " --replace Replace the currently running window manager\n"
+msgstr " --replace 替æ›ç›®å‰åŸ·è¡Œçš„視窗管ç†å“¡\n"
+
+#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..."
+#. aligned still, if you have to, make a new line with \n and 22 spaces. It's
+#. fine to leave it as FILE though.
+#: openbox/openbox.c:536
+msgid " --config-file FILE Specify the path to the config file to use\n"
+msgstr " --config-file <檔案> 指定è¦ä½¿ç”¨çš„設定檔路徑\n"
+
+#: openbox/openbox.c:537
+msgid " --sm-disable Disable connection to the session manager\n"
+msgstr " --sm-disable åœç”¨èˆ‡åŸ·è¡ŒéšŽæ®µç®¡ç†ç¨‹å¼çš„連çµ\n"
+
+#: openbox/openbox.c:538
+msgid ""
+"\n"
+"Passing messages to a running Openbox instance:\n"
+msgstr ""
+"\n"
+"傳éžè¨Šæ¯åˆ°åŸ·è¡Œä¸­çš„ Openbox 實體:\n"
+
+#: openbox/openbox.c:539
+msgid " --reconfigure Reload Openbox's configuration\n"
+msgstr " --reconfigure é‡æ–°è¼‰å…¥ Openbox é…ç½®\n"
+
+#: openbox/openbox.c:540
+msgid " --restart Restart Openbox\n"
+msgstr " --restart é‡æ–°å•Ÿå‹• Openbox\n"
+
+#: openbox/openbox.c:541
+msgid " --exit Exit Openbox\n"
+msgstr " --exit çµæŸ Openbox\n"
+
+#: openbox/openbox.c:542
+msgid ""
+"\n"
+"Debugging options:\n"
+msgstr ""
+"\n"
+"åµéŒ¯é¸é …:\n"
+
+#: openbox/openbox.c:543
+msgid " --sync Run in synchronous mode\n"
+msgstr " --sync 在åŒæ­¥æ¨¡å¼ä¸­é‹è¡Œ\n"
+
+#: openbox/openbox.c:544
+msgid " --startup CMD Run CMD after starting\n"
+msgstr ""
+
+#: openbox/openbox.c:545
+msgid " --debug Display debugging output\n"
+msgstr " --debug 顯示åµéŒ¯è¼¸å‡º\n"
+
+#: openbox/openbox.c:546
+msgid " --debug-focus Display debugging output for focus handling\n"
+msgstr " --debug-focus 顯示焦點處ç†çš„åµéŒ¯è¼¸å‡º\n"
+
+#: openbox/openbox.c:547
+msgid " --debug-session Display debugging output for session management\n"
+msgstr ""
+
+#: openbox/openbox.c:548
+msgid " --debug-xinerama Split the display into fake xinerama screens\n"
+msgstr " --debug-xinerama 分割顯示以進入å‡é€ çš„ xinerama 螢幕\n"
+
+#: openbox/openbox.c:549
+#, c-format
+msgid ""
+"\n"
+"Please report bugs at %s\n"
+msgstr ""
+"\n"
+"è«‹å‘ %s 報告錯誤\n"
+
+#: openbox/openbox.c:632 openbox/openbox.c:666
+#, c-format
+msgid "%s requires an argument\n"
+msgstr "%s 需è¦ä¸€å€‹åƒæ•¸\n"
+
+#: openbox/openbox.c:709
+#, c-format
+msgid "Invalid command line argument \"%s\"\n"
+msgstr "無效的命令列引數「%sã€\n"
+
+#: openbox/screen.c:106 openbox/screen.c:191
+#, c-format
+msgid "A window manager is already running on screen %d"
+msgstr "螢幕 %d 中已經有視窗管ç†å“¡åœ¨é‹è¡Œ"
+
+#: openbox/screen.c:127
+#, c-format
+msgid "Could not acquire window manager selection on screen %d"
+msgstr "無法於螢幕 %d ç²é¸ç‚ºè¦–窗管ç†å“¡"
+
+#: openbox/screen.c:150
+#, c-format
+msgid "The WM on screen %d is not exiting"
+msgstr "螢幕 %d 中的視窗管ç†å“¡ä¸¦æœªé›¢é–‹"
+
+#. TRANSLATORS: If you need to specify a different order of the
+#. arguments, you can use %1$d for the first one and %2$d for the
+#. second one. For example,
+#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..."
+#: openbox/screen.c:418
+#, fuzzy, c-format
+msgid ""
+"Openbox is configured for %d desktop, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgid_plural ""
+"Openbox is configured for %d desktops, but the current session has %d. "
+"Overriding the Openbox configuration."
+msgstr[0] ""
+"Openbox 原先被設定為使用 %d 個桌é¢ï¼Œä½†ç›®å‰çš„作業階段有其他程å¼è®Šæ›´è¨­å®šç‚º %d "
+"個,因此忽略 Openbox 的設定"
+
+#: openbox/screen.c:1205
+#, c-format
+msgid "desktop %i"
+msgstr "æ¡Œé¢ %i"
+
+#: openbox/startupnotify.c:241
+#, c-format
+msgid "Running %s"
+msgstr "正在é‹è¡Œ %s"
+
+#: openbox/translate.c:59
+#, c-format
+msgid "Invalid modifier key \"%s\" in key/mouse binding"
+msgstr "與éµç›¤/滑鼠組åˆçš„輔助按éµã€Œ%sã€ç„¡æ•ˆ"
+
+#: openbox/translate.c:138
+#, c-format
+msgid "Invalid key code \"%s\" in key binding"
+msgstr "與按éµçµ„åˆçš„éµç¢¼ã€Œ%sã€ç„¡æ•ˆ"
+
+#: openbox/translate.c:145
+#, c-format
+msgid "Invalid key name \"%s\" in key binding"
+msgstr "與按éµçµ„åˆçš„éµå「%sã€ç„¡æ•ˆ"
+
+#: openbox/translate.c:151
+#, c-format
+msgid "Requested key \"%s\" does not exist on the display"
+msgstr "è¦æ±‚的按éµã€Œ%sã€ä¸å­˜åœ¨æ–¼ç•«é¢ä¹‹ä¸­"
+
+#: openbox/prompt.c:153
+msgid "OK"
+msgstr "確定"
+
+#, fuzzy
+#~ msgid "Openbox"
+#~ msgstr "離開 Openbox"
+
+#~ msgid "--config-file requires an argument\n"
+#~ msgstr "--config-file 需è¦ä¸€å€‹åƒæ•¸\n"
+
+#~ msgid ""
+#~ "The SessionLogout action is not available since Openbox was built without "
+#~ "session management support"
+#~ msgstr ""
+#~ "SessionLogout 動作無法使用,因為 Openbox 在編譯時沒有使用作業階段管ç†æ”¯æ´"
+
+#~ msgid "Unable to save the session to \"%s\": %s"
+#~ msgstr "無法儲存執行階段到「%sã€ï¼š%s"
+
+#~ msgid "Error while saving the session to \"%s\": %s"
+#~ msgstr "當儲存執行階段「%sã€æ™‚發生錯誤:%s"
+
+#~ msgid "Not connected to a session manager"
+#~ msgstr "沒有連接到作業階段管ç†å“¡"
+
+#~ msgid "X Error: %s"
+#~ msgstr "X 錯誤:%s"
+
+#~ msgid "Failed to execute \"%s\": %s"
+#~ msgstr "執行「%sã€æ™‚失敗:%s"
+
+#~ msgid "Invalid use of action \"%s\". Action will be ignored."
+#~ msgstr "使用的動作「%sã€ç„¡æ•ˆã€‚動作將被忽略。"
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..9fc5fe8
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,6 @@
+files=$(wildcard *.c)
+
+all: $(files:.c=)
+
+%: %.c
+ $(CC) `pkg-config --cflags --libs glib-2.0` $(CFLAGS) -o $@ $^ -lX11 -lXext -L/usr/X11R6/lib -I/usr/X11R6/include
diff --git a/tests/aspect.c b/tests/aspect.c
new file mode 100644
index 0000000..1ae3a85
--- /dev/null
+++ b/tests/aspect.c
@@ -0,0 +1,79 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ aspect.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main () {
+ XSetWindowAttributes xswa;
+ unsigned long xswamask;
+ Display *display;
+ Window win;
+ XEvent report;
+ int x=10,y=10,h=100,w=400;
+ XSizeHints size;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ xswa.win_gravity = StaticGravity;
+ xswamask = CWWinGravity;
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, xswamask, &xswa);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ size.flags = PAspect;
+ size.min_aspect.x = 3;
+ size.min_aspect.y = 3;
+ size.max_aspect.x = 3;
+ size.max_aspect.y = 3;
+ XSetWMNormalHints(display, win, &size);
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ XSelectInput(display, win, ExposureMask | StructureNotifyMask);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/big.c b/tests/big.c
new file mode 100644
index 0000000..bc17c5a
--- /dev/null
+++ b/tests/big.c
@@ -0,0 +1,68 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ big.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ XEvent msg;
+ int x=10,y=10,h=2000,w=2000;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+ sleep(2);
+
+ XSelectInput(display, win, ExposureMask | StructureNotifyMask);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/borderchange.c b/tests/borderchange.c
new file mode 100644
index 0000000..56d5500
--- /dev/null
+++ b/tests/borderchange.c
@@ -0,0 +1,107 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ borderchange.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ XEvent msg;
+ int x=10,y=10,h=200,w=200;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+ XSelectInput(display, win, ExposureMask | StructureNotifyMask);
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ while (XPending(display)) {
+ XNextEvent(display, &report);
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+ }
+ sleep(2);
+
+ printf("setting border to 0\n");
+ XSetWindowBorderWidth(display, win, 0);
+ XFlush(display);
+
+ while (XPending(display)) {
+ XNextEvent(display, &report);
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+ }
+ sleep(2);
+
+ printf("setting border to 50\n");
+ XSetWindowBorderWidth(display, win, 50);
+ XFlush(display);
+
+ while (XPending(display)) {
+ XNextEvent(display, &report);
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 0;
+}
diff --git a/tests/confignotify.c b/tests/confignotify.c
new file mode 100644
index 0000000..4bb09f8
--- /dev/null
+++ b/tests/confignotify.c
@@ -0,0 +1,89 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ confignotify.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ XEvent msg;
+ int x=10,y=10,h=100,w=100;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 0, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XSelectInput(display, win, (ExposureMask | StructureNotifyMask |
+ GravityNotify));
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ sleep(1);
+ XResizeWindow(display, win, w+5, h+5);
+ XMoveWindow(display, win, x, y);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case MapNotify:
+ printf("map notify\n");
+ break;
+ case Expose:
+ printf("exposed\n");
+ break;
+ case GravityNotify:
+ printf("gravity notify event 0x%x window 0x%x x %d y %d\n",
+ report.xgravity.event, report.xgravity.window,
+ report.xgravity.x, report.xgravity.y);
+ break;
+ case ConfigureNotify: {
+ int se = report.xconfigure.send_event;
+ int event = report.xconfigure.event;
+ int window = report.xconfigure.window;
+ int x = report.xconfigure.x;
+ int y = report.xconfigure.y;
+ int w = report.xconfigure.width;
+ int h = report.xconfigure.height;
+ int bw = report.xconfigure.border_width;
+ int above = report.xconfigure.above;
+ int or = report.xconfigure.override_redirect;
+ printf("confignotify send %d ev 0x%x win 0x%x %i,%i-%ix%i bw %i\n"
+ " above 0x%x ovrd %d\n",
+ se,event,window,x,y,w,h,bw,above,or);
+ break;
+ }
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/confignotifymax.c b/tests/confignotifymax.c
new file mode 100644
index 0000000..0debf5e
--- /dev/null
+++ b/tests/confignotifymax.c
@@ -0,0 +1,97 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ confignotify.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ XEvent msg;
+ Atom _net_max[2],_net_state;
+ int x=10,y=10,h=100,w=100;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ _net_state = XInternAtom(display, "_NET_WM_STATE", False);
+ _net_max[0] = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
+ _net_max[1] = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 0, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+ XChangeProperty(display, win, _net_state, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&_net_max, 2);
+
+ XSelectInput(display, win, (ExposureMask | StructureNotifyMask |
+ GravityNotify));
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ //sleep(1);
+ //XResizeWindow(display, win, w+5, h+5);
+ //XMoveWindow(display, win, x, y);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case MapNotify:
+ printf("map notify\n");
+ break;
+ case Expose:
+ printf("exposed\n");
+ break;
+ case GravityNotify:
+ printf("gravity notify event 0x%x window 0x%x x %d y %d\n",
+ report.xgravity.event, report.xgravity.window,
+ report.xgravity.x, report.xgravity.y);
+ break;
+ case ConfigureNotify: {
+ int se = report.xconfigure.send_event;
+ int event = report.xconfigure.event;
+ int window = report.xconfigure.window;
+ int x = report.xconfigure.x;
+ int y = report.xconfigure.y;
+ int w = report.xconfigure.width;
+ int h = report.xconfigure.height;
+ int bw = report.xconfigure.border_width;
+ int above = report.xconfigure.above;
+ int or = report.xconfigure.override_redirect;
+ printf("confignotify send %d ev 0x%x win 0x%x %i,%i-%ix%i bw %i\n"
+ " above 0x%x ovrd %d\n",
+ se,event,window,x,y,w,h,bw,above,or);
+ break;
+ }
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/cursorio.c b/tests/cursorio.c
new file mode 100644
index 0000000..527d70c
--- /dev/null
+++ b/tests/cursorio.c
@@ -0,0 +1,57 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ cursorio.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/cursorfont.h>
+
+int main () {
+ Display *display;
+ Window win, child;
+ XEvent report;
+ int x=10,y=10,h=100,w=400,b=10;
+ XSetWindowAttributes a;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, b, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ a.cursor = XCreateFontCursor(display, XC_watch);
+ child = XCreateWindow(display, win,
+ x+w/8, y+h/8, 3*w/4, 3*h/4, 0,
+ CopyFromParent, InputOnly,
+ CopyFromParent, CWCursor, &a);
+
+ XMapWindow(display, child);
+ XMapWindow(display, win);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/duplicatesession.c b/tests/duplicatesession.c
new file mode 100644
index 0000000..5abe995
--- /dev/null
+++ b/tests/duplicatesession.c
@@ -0,0 +1,66 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ duplicatesession.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main (int argc, char **argv) {
+ Display *display;
+ Window win1, win2;
+ XEvent report;
+ int x=10,y=10,h=100,w=400;
+ XSizeHints size;
+ XTextProperty name;
+ Atom sm_id, enc;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ sm_id = XInternAtom(display,"SM_CLIENT_ID",False);
+ enc = XInternAtom(display,"STRING",False);
+
+ win1 = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+ win2 = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ XSetWindowBackground(display,win1,WhitePixel(display,0));
+ XSetWindowBackground(display,win2,BlackPixel(display,0));
+
+ XChangeProperty(display, win1, sm_id, enc, 8,
+ PropModeAppend, "abcdefg", strlen("abcdefg"));
+ XChangeProperty(display, win2, sm_id, enc, 8,
+ PropModeAppend, "abcdefg", strlen("abcdefg"));
+
+ XFlush(display);
+ XMapWindow(display, win1);
+ XMapWindow(display, win2);
+
+ while (1)
+ XNextEvent(display, &report);
+
+ return 1;
+}
diff --git a/tests/extentsrequest.c b/tests/extentsrequest.c
new file mode 100644
index 0000000..055624d
--- /dev/null
+++ b/tests/extentsrequest.c
@@ -0,0 +1,133 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ extentsrequest.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+void request (Display *display, Atom _request, Atom _extents, Window win) {
+ XEvent msg;
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = _request;
+ msg.xclient.display = display;
+ msg.xclient.window = win;
+ msg.xclient.format = 32;
+ msg.xclient.data.l[0] = 0l;
+ msg.xclient.data.l[1] = 0l;
+ msg.xclient.data.l[2] = 0l;
+ msg.xclient.data.l[3] = 0l;
+ msg.xclient.data.l[4] = 0l;
+ XSendEvent(display, RootWindow(display, 0), False,
+ SubstructureNotifyMask | SubstructureRedirectMask, &msg);
+ XFlush(display);
+}
+
+void reply (Display* display, Atom _extents) {
+ printf(" waiting for extents\n");
+ while (1) {
+ XEvent report;
+ XNextEvent(display, &report);
+
+ if (report.type == PropertyNotify &&
+ report.xproperty.atom == _extents)
+ {
+ Atom ret_type;
+ int ret_format;
+ unsigned long ret_items, ret_bytesleft;
+ unsigned long *prop_return;
+ XGetWindowProperty(display, report.xproperty.window, _extents, 0, 4,
+ False, XA_CARDINAL, &ret_type, &ret_format,
+ &ret_items, &ret_bytesleft,
+ (unsigned char**) &prop_return);
+ if (ret_type == XA_CARDINAL && ret_format == 32 && ret_items == 4)
+ printf(" got new extents %d, %d, %d, %d\n",
+ prop_return[0], prop_return[1], prop_return[2],
+ prop_return[3]);
+ break;
+ }
+ }
+}
+
+int main () {
+ Display *display;
+ Window win;
+ Atom _request, _extents, _type, _normal, _desktop, _state;
+ Atom _state_fs, _state_mh, _state_mv;
+ int x=10,y=10,h=100,w=400;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ _type = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
+ _normal = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
+ _desktop = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
+ _request = XInternAtom(display, "_NET_REQUEST_FRAME_EXTENTS", False);
+ _extents = XInternAtom(display, "_NET_FRAME_EXTENTS", False);
+ _state = XInternAtom(display, "_NET_WM_STATE", False);
+ _state_fs = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False);
+ _state_mh = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
+ _state_mv = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+ XSelectInput(display, win, PropertyChangeMask);
+
+ printf("requesting for type normal\n");
+ XChangeProperty(display, win, _type, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&_normal, 1);
+ request(display, _request, _extents, win);
+ reply(display, _extents);
+
+ printf("requesting for type normal+fullscreen\n");
+ XChangeProperty(display, win, _type, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&_normal, 1);
+ XChangeProperty(display, win, _state, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&_state_fs, 1);
+ request(display, _request, _extents, win);
+ reply(display, _extents);
+
+ printf("requesting for type normal+maxv\n");
+ XChangeProperty(display, win, _type, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&_normal, 1);
+ XChangeProperty(display, win, _state, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&_state_mv, 1);
+ request(display, _request, _extents, win);
+ reply(display, _extents);
+
+ printf("requesting for type normal+maxh\n");
+ XChangeProperty(display, win, _type, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&_normal, 1);
+ XChangeProperty(display, win, _state, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&_state_mh, 1);
+ request(display, _request, _extents, win);
+ reply(display, _extents);
+
+ printf("requesting for type desktop\n");
+ XChangeProperty(display, win, _type, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&_desktop, 1);
+ request(display, _request, _extents, win);
+ reply(display, _extents);
+
+ return 1;
+}
diff --git a/tests/fakeunmap.c b/tests/fakeunmap.c
new file mode 100644
index 0000000..d30a917
--- /dev/null
+++ b/tests/fakeunmap.c
@@ -0,0 +1,60 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ fakeunmap.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ XEvent msg;
+ int x=50,y=50,h=100,w=400;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+ usleep(10000);
+
+ msg.type = UnmapNotify;
+ msg.xunmap.display = display;
+ msg.xunmap.event = RootWindow(display, DefaultScreen(display));
+ msg.xunmap.window = win;
+ msg.xunmap.from_configure = False;
+ XSendEvent(display, RootWindow(display, DefaultScreen(display)), False,
+ SubstructureRedirectMask|SubstructureNotifyMask, &msg);
+ usleep(10000);
+
+ XUnmapWindow(display, win);
+ XSync(display, False);
+
+ return 1;
+}
diff --git a/tests/fallback.c b/tests/fallback.c
new file mode 100644
index 0000000..721ed31
--- /dev/null
+++ b/tests/fallback.c
@@ -0,0 +1,64 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ fallback.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window one, two;
+ XEvent report;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ one = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,200,200, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ two = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,150,150, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ XSetWindowBackground(display,one,WhitePixel(display,0));
+ XSetWindowBackground(display,two,BlackPixel(display,0));
+
+ XSetTransientForHint(display, two, one);
+
+ XMapWindow(display, one);
+ XFlush(display);
+ usleep(1000);
+
+ XMapWindow(display, two);
+ XFlush(display);
+ usleep(1000);
+
+ XDestroyWindow(display, two);
+ XFlush(display);
+ usleep(1000);
+
+ XDestroyWindow(display, one);
+ XSync(display, False);
+
+ return 1;
+}
diff --git a/tests/focusout.c b/tests/focusout.c
new file mode 100644
index 0000000..03ba56f
--- /dev/null
+++ b/tests/focusout.c
@@ -0,0 +1,224 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ focusout.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main () {
+ Display *display;
+ Window win, child;
+ XEvent report;
+ Atom _net_fs, _net_state;
+ XEvent msg;
+ int x=50,y=50,h=100,w=400;
+ XWMHints hint;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+ child = XCreateWindow(display, win,
+ 10, 10, w-20, h-20, 0, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+ XSetWindowBackground(display,child,BlackPixel(display,0));
+
+ XSelectInput(display, win,
+ FocusChangeMask|EnterWindowMask|LeaveWindowMask);
+ XMapWindow(display, win);
+ XMapWindow(display, child);
+
+ while (1) {
+ const char *mode, *detail;
+
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case ButtonPress:
+ printf("button press\n");
+ printf("type : %d\n", report.xbutton.type);
+ printf("serial : %d\n", report.xbutton.serial);
+ printf("send_event : %d\n", report.xbutton.send_event);
+ printf("display : 0x%x\n", report.xbutton.display);
+ printf("window : 0x%x\n", report.xbutton.window);
+ printf("root : 0x%x\n", report.xbutton.root);
+ printf("subwindow : 0x%x\n", report.xbutton.subwindow);
+ printf("time : %d\n", report.xbutton.time);
+ printf("x, y : %d, %d\n", report.xbutton.x,
+ report.xbutton.y);
+ printf("rootx, rooty: %d, %d\n", report.xbutton.x_root,
+ report.xbutton.y_root);
+ printf("state : 0x%x\n", report.xbutton.state);
+ printf("button : %d\n", report.xbutton.button);
+ printf("same_screen : %d\n", report.xbutton.same_screen);
+ printf("---\n");
+ break;
+ case MotionNotify:
+ printf("motion\n");
+ printf("type : %d\n", report.xmotion.type);
+ printf("serial : %d\n", report.xmotion.serial);
+ printf("send_event : %d\n", report.xmotion.send_event);
+ printf("display : 0x%x\n", report.xmotion.display);
+ printf("window : 0x%x\n", report.xmotion.window);
+ printf("root : 0x%x\n", report.xmotion.root);
+ printf("subwindow : 0x%x\n", report.xmotion.subwindow);
+ printf("time : %d\n", report.xmotion.time);
+ printf("x, y : %d, %d\n", report.xmotion.x,
+ report.xmotion.y);
+ printf("rootx, rooty: %d, %d\n", report.xmotion.x_root,
+ report.xmotion.y_root);
+ printf("state : 0x%x\n", report.xmotion.state);
+ printf("is_hint : %d\n", report.xmotion.is_hint);
+ printf("same_screen : %d\n", report.xmotion.same_screen);
+ printf("---\n");
+ if (XGrabPointer(display, win, False, ButtonReleaseMask,
+ GrabModeAsync, GrabModeAsync, None, None,
+ report.xmotion.time) == GrabSuccess)
+ printf("GrabSuccess\n");
+ else
+ printf("GrabFail\n");
+ break;
+ case ButtonRelease:
+ XUngrabPointer(display, report.xbutton.time);
+ break;
+ case FocusIn:
+ switch (report.xfocus.mode) {
+ case NotifyNormal: mode = "NotifyNormal"; break;
+ case NotifyGrab: mode = "NotifyGrab"; break;
+ case NotifyUngrab: mode = "NotifyUngrab"; break;
+ }
+
+ switch (report.xfocus.detail) {
+ case NotifyAncestor: detail = "NotifyAncestor"; break;
+ case NotifyVirtual: detail = "NotifyVirtual"; break;
+ case NotifyInferior: detail = "NotifyInferior"; break;
+ case NotifyNonlinear: detail = "NotifyNonlinear"; break;
+ case NotifyNonlinearVirtual:
+ detail = "NotifyNonlinearVirtual"; break;
+ case NotifyPointer: detail = "NotifyPointer"; break;
+ case NotifyPointerRoot: detail = "NotifyPointerRoot"; break;
+ case NotifyDetailNone: detail = "NotifyDetailNone"; break;
+ }
+ printf("focusin\n");
+ printf("type : %d\n", report.xfocus.type);
+ printf("serial : %d\n", report.xfocus.serial);
+ printf("send_event: %d\n", report.xfocus.send_event);
+ printf("display : 0x%x\n", report.xfocus.display);
+ printf("window : 0x%x\n", report.xfocus.window);
+ printf("mode : %s\n", mode);
+ printf("detail : %s\n", detail);
+ printf("---\n");
+ break;
+ case FocusOut:
+ switch (report.xfocus.mode) {
+ case NotifyNormal: mode = "NotifyNormal"; break;
+ case NotifyGrab: mode = "NotifyGrab"; break;
+ case NotifyUngrab: mode = "NotifyUngrab"; break;
+ }
+
+ switch (report.xfocus.detail) {
+ case NotifyAncestor: detail = "NotifyAncestor"; break;
+ case NotifyVirtual: detail = "NotifyVirtual"; break;
+ case NotifyInferior: detail = "NotifyInferior"; break;
+ case NotifyNonlinear: detail = "NotifyNonlinear"; break;
+ case NotifyNonlinearVirtual:
+ detail = "NotifyNonlinearVirtual"; break;
+ case NotifyPointer: detail = "NotifyPointer"; break;
+ case NotifyPointerRoot: detail = "NotifyPointerRoot"; break;
+ case NotifyDetailNone: detail = "NotifyDetailNone"; break;
+ }
+ printf("focusout\n");
+ printf("type : %d\n", report.xfocus.type);
+ printf("serial : %d\n", report.xfocus.serial);
+ printf("send_event: %d\n", report.xfocus.send_event);
+ printf("display : 0x%x\n", report.xfocus.display);
+ printf("window : 0x%x\n", report.xfocus.window);
+ printf("mode : %s\n", mode);
+ printf("detail : %s\n", detail);
+ printf("---\n");
+ break;
+ case EnterNotify:
+ switch (report.xcrossing.mode) {
+ case NotifyNormal: mode = "NotifyNormal"; break;
+ case NotifyGrab: mode = "NotifyGrab"; break;
+ case NotifyUngrab: mode = "NotifyUngrab"; break;
+ }
+
+ switch (report.xcrossing.detail) {
+ case NotifyAncestor: detail = "NotifyAncestor"; break;
+ case NotifyVirtual: detail = "NotifyVirtual"; break;
+ case NotifyInferior: detail = "NotifyInferior"; break;
+ case NotifyNonlinear: detail = "NotifyNonlinear"; break;
+ case NotifyNonlinearVirtual:
+ detail = "NotifyNonlinearVirtual"; break;
+ case NotifyPointer: detail = "NotifyPointer"; break;
+ case NotifyPointerRoot: detail = "NotifyPointerRoot"; break;
+ case NotifyDetailNone: detail = "NotifyDetailNone"; break;
+ }
+ printf("enternotify\n");
+ printf("type : %d\n", report.xcrossing.type);
+ printf("serial : %d\n", report.xcrossing.serial);
+ printf("send_event: %d\n", report.xcrossing.send_event);
+ printf("display : 0x%x\n", report.xcrossing.display);
+ printf("window : 0x%x\n", report.xcrossing.window);
+ printf("mode : %s\n", mode);
+ printf("detail : %s\n", detail);
+ printf("---\n");
+ break;
+ case LeaveNotify:
+ switch (report.xcrossing.mode) {
+ case NotifyNormal: mode = "NotifyNormal"; break;
+ case NotifyGrab: mode = "NotifyGrab"; break;
+ case NotifyUngrab: mode = "NotifyUngrab"; break;
+ }
+
+ switch (report.xcrossing.detail) {
+ case NotifyAncestor: detail = "NotifyAncestor"; break;
+ case NotifyVirtual: detail = "NotifyVirtual"; break;
+ case NotifyInferior: detail = "NotifyInferior"; break;
+ case NotifyNonlinear: detail = "NotifyNonlinear"; break;
+ case NotifyNonlinearVirtual:
+ detail = "NotifyNonlinearVirtual"; break;
+ case NotifyPointer: detail = "NotifyPointer"; break;
+ case NotifyPointerRoot: detail = "NotifyPointerRoot"; break;
+ case NotifyDetailNone: detail = "NotifyDetailNone"; break;
+ }
+ printf("leavenotify\n");
+ printf("type : %d\n", report.xcrossing.type);
+ printf("serial : %d\n", report.xcrossing.serial);
+ printf("send_event: %d\n", report.xcrossing.send_event);
+ printf("display : 0x%x\n", report.xcrossing.display);
+ printf("window : 0x%x\n", report.xcrossing.window);
+ printf("mode : %s\n", mode);
+ printf("detail : %s\n", detail);
+ printf("---\n");
+ break;
+ }
+ }
+
+ return 1;
+}
diff --git a/tests/fullscreen.c b/tests/fullscreen.c
new file mode 100644
index 0000000..6907cab
--- /dev/null
+++ b/tests/fullscreen.c
@@ -0,0 +1,102 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ fullscreen.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ Atom _net_fs, _net_state;
+ XEvent msg;
+ int x=10,y=10,h=100,w=400;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ _net_state = XInternAtom(display, "_NET_WM_STATE", False);
+ _net_fs = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False);
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+ sleep(2);
+
+ printf("fullscreen\n");
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = _net_state;
+ msg.xclient.display = display;
+ msg.xclient.window = win;
+ msg.xclient.format = 32;
+ msg.xclient.data.l[0] = 2; // toggle
+ msg.xclient.data.l[1] = _net_fs;
+ msg.xclient.data.l[2] = 0l;
+ msg.xclient.data.l[3] = 0l;
+ msg.xclient.data.l[4] = 0l;
+ XSendEvent(display, RootWindow(display, 0), False,
+ SubstructureNotifyMask | SubstructureRedirectMask, &msg);
+ XFlush(display);
+ sleep(2);
+
+ printf("restore\n");
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = _net_state;
+ msg.xclient.display = display;
+ msg.xclient.window = win;
+ msg.xclient.format = 32;
+ msg.xclient.data.l[0] = 2; // toggle
+ msg.xclient.data.l[1] = _net_fs;
+ msg.xclient.data.l[2] = 0l;
+ msg.xclient.data.l[3] = 0l;
+ msg.xclient.data.l[4] = 0l;
+ XSendEvent(display, RootWindow(display, 0), False,
+ SubstructureNotifyMask | SubstructureRedirectMask, &msg);
+
+ XSelectInput(display, win, ExposureMask | StructureNotifyMask);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/grav.c b/tests/grav.c
new file mode 100644
index 0000000..772fec2
--- /dev/null
+++ b/tests/grav.c
@@ -0,0 +1,80 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ grav.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ int x=10,y=10,h=100,w=400,b=10;
+ XSizeHints *hints;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, b, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ hints = XAllocSizeHints();
+ hints->flags = PWinGravity;
+ hints->win_gravity = SouthEastGravity;
+ XSetWMNormalHints(display, win, hints);
+ XFree(hints);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ w = 600;
+ h = 160;
+ XMoveResizeWindow(display, win, 1172-w-b*2, 668-h-b*2, w, h);
+ XFlush(display);
+ sleep(1);
+ XResizeWindow(display, win, 900, 275);
+
+ XSelectInput(display, win, ExposureMask | StructureNotifyMask);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/groupmodal.c b/tests/groupmodal.c
new file mode 100644
index 0000000..12057eb
--- /dev/null
+++ b/tests/groupmodal.c
@@ -0,0 +1,80 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ grouptran.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window one, two, group;
+ XEvent report;
+ XWMHints *wmhints;
+ Atom state, modal;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ state = XInternAtom(display, "_NET_WM_STATE", True);
+ modal = XInternAtom(display, "_NET_WM_STATE_MODAL", True);
+
+ group = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,1,1, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ one = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,300,300, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ two = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,100,100, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ XSetWindowBackground(display,one,WhitePixel(display,0));
+ XSetWindowBackground(display,two,BlackPixel(display,0));
+
+ XSetTransientForHint(display, two, RootWindow(display,0));
+ XChangeProperty(display, two, state, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&modal, 1);
+
+ wmhints = XAllocWMHints();
+
+ wmhints->flags = WindowGroupHint;
+ wmhints->window_group = group;
+
+ XSetWMHints(display, one, wmhints);
+ XSetWMHints(display, two, wmhints);
+
+ XFree(wmhints);
+
+ XMapWindow(display, one);
+ XFlush(display);
+ sleep(1);
+ XMapWindow(display, two);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/grouptran.c b/tests/grouptran.c
new file mode 100644
index 0000000..320da2f
--- /dev/null
+++ b/tests/grouptran.c
@@ -0,0 +1,72 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ grouptran.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window one, two, group;
+ XEvent report;
+ XWMHints *wmhints;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ group = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,1,1, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ one = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,100,100, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ two = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,100,100, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ XSetWindowBackground(display,one,WhitePixel(display,0));
+ XSetWindowBackground(display,two,BlackPixel(display,0));
+
+ XSetTransientForHint(display, two, RootWindow(display,0));
+
+ wmhints = XAllocWMHints();
+
+ wmhints->flags = WindowGroupHint;
+ wmhints->window_group = group;
+
+ XSetWMHints(display, one, wmhints);
+ XSetWMHints(display, two, wmhints);
+
+ XFree(wmhints);
+
+ XMapWindow(display, one);
+ XMapWindow(display, two);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/grouptran2.c b/tests/grouptran2.c
new file mode 100644
index 0000000..4cd6c58
--- /dev/null
+++ b/tests/grouptran2.c
@@ -0,0 +1,79 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ grouptran2.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window main, grouptran, child, group;
+ XEvent report;
+ XWMHints *wmhints;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ group = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,1,1, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ main = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,100,100, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ grouptran = XCreateWindow(display, RootWindow(display, 0),
+ 10,10,80,180, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ child = XCreateWindow(display, RootWindow(display, 0),
+ 20,20,60,60, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ XSetWindowBackground(display,main,WhitePixel(display,0));
+ XSetWindowBackground(display,grouptran,BlackPixel(display,0));
+ XSetWindowBackground(display,child,WhitePixel(display,0));
+
+ XSetTransientForHint(display, grouptran, RootWindow(display,0));
+ XSetTransientForHint(display, child, grouptran);
+
+ wmhints = XAllocWMHints();
+
+ wmhints->flags = WindowGroupHint;
+ wmhints->window_group = group;
+
+ XSetWMHints(display, main, wmhints);
+ XSetWMHints(display, grouptran, wmhints);
+ XSetWMHints(display, child, wmhints);
+
+ XFree(wmhints);
+
+ XMapWindow(display, main);
+ XMapWindow(display, grouptran);
+ XMapWindow(display, child);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/grouptrancircular.c b/tests/grouptrancircular.c
new file mode 100644
index 0000000..3853aef
--- /dev/null
+++ b/tests/grouptrancircular.c
@@ -0,0 +1,73 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ grouptrancircular.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window one, two, group;
+ XEvent report;
+ XWMHints *wmhints;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ group = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,1,1, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ one = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,100,100, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ two = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,100,100, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ XSetWindowBackground(display,one,WhitePixel(display,0));
+ XSetWindowBackground(display,two,BlackPixel(display,0));
+
+ XSetTransientForHint(display, one, RootWindow(display,0));
+ XSetTransientForHint(display, two, RootWindow(display,0));
+
+ wmhints = XAllocWMHints();
+
+ wmhints->flags = WindowGroupHint;
+ wmhints->window_group = group;
+
+ XSetWMHints(display, one, wmhints);
+ XSetWMHints(display, two, wmhints);
+
+ XFree(wmhints);
+
+ XMapWindow(display, one);
+ XMapWindow(display, two);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/grouptrancircular2.c b/tests/grouptrancircular2.c
new file mode 100644
index 0000000..1956c8f
--- /dev/null
+++ b/tests/grouptrancircular2.c
@@ -0,0 +1,79 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ grouptrancircular.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window one, two, three, group;
+ XEvent report;
+ XWMHints *wmhints;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ group = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,1,1, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ one = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,100,100, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ two = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,100,100, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ three = XCreateWindow(display, RootWindow(display, 0),
+ 0,0,100,100, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ XSetWindowBackground(display,one,WhitePixel(display,0));
+ XSetWindowBackground(display,two,BlackPixel(display,0));
+
+ XSetTransientForHint(display, one, RootWindow(display,0));
+ XSetTransientForHint(display, two, one);
+ XSetTransientForHint(display, three, two);
+
+ wmhints = XAllocWMHints();
+
+ wmhints->flags = WindowGroupHint;
+ wmhints->window_group = group;
+
+ XSetWMHints(display, one, wmhints);
+ XSetWMHints(display, two, wmhints);
+ XSetWMHints(display, three, wmhints);
+
+ XFree(wmhints);
+
+ XMapWindow(display, one);
+ XMapWindow(display, two);
+ XMapWindow(display, three);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/hideshow.py b/tests/hideshow.py
new file mode 100755
index 0000000..2e7fc3a
--- /dev/null
+++ b/tests/hideshow.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+
+import pygtk
+import gtk
+import gobject
+pygtk.require('2.0')
+
+class FolderSelector(gtk.Window):
+ def __init__(self, jules):
+ gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
+ print "init folder selector", self, jules
+ self.set_title("Select Folder")
+ self.jules = jules
+
+ self.set_size_request(140, 200)
+
+ self.list_model = gtk.ListStore(gobject.TYPE_STRING)
+ self.tree = gtk.TreeView(self.list_model)
+ self.folder_column = gtk.TreeViewColumn('Folder')
+ self.tree.append_column(self.folder_column)
+
+ self.folder_cell = gtk.CellRendererText()
+ self.folder_column.pack_start(self.folder_cell, True)
+ self.folder_column.add_attribute(self.folder_cell, 'text', 0)
+
+ self.tree.set_search_column(0)
+
+ self.icon_theme = gtk.icon_theme_get_default()
+
+ self.add(self.tree)
+ self.show_all()
+ self.tree.columns_autosize()
+ print "done init"
+
+class Jules(gtk.Window):
+ def __init__(self):
+ gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
+ self.set_title("Jules")
+ self.set_size_request(150, 320)
+ self.connect("delete_event", self.on_delete_event)
+ self.connect("destroy", self.on_destroy)
+ self.scroll = gtk.ScrolledWindow()
+
+ self.tree_model = gtk.TreeStore(gobject.TYPE_STRING,
+ gobject.TYPE_STRING)
+ self.tree = gtk.TreeView(self.tree_model)
+ self.file_column = gtk.TreeViewColumn('name', gtk.CellRendererText(),
+ markup=0)
+ self.file_column.set_sort_indicator(True)
+ self.file_column.set_clickable(True)
+ self.file_column.set_sort_column_id(1)
+ self.tree.append_column(self.file_column)
+ self.tree.set_headers_clickable(True)
+ self.tree.set_search_column(0)
+
+ self.scroll.add(self.tree)
+ self.add(self.scroll)
+ self.show_all()
+
+ self.project_selector = FolderSelector(self)
+ self.project_selector.hide()
+ self.project_selector.hide()
+
+ self.project_selector.show()
+
+ def on_delete_event(self, widget, event):
+ return False
+
+ def on_destroy(self, widget):
+ gtk.main_quit()
+
+ def run(self):
+ gtk.main()
+
+
+if __name__ == "__main__":
+ jules = Jules()
+ jules.run()
diff --git a/tests/iconifydelay.c b/tests/iconifydelay.c
new file mode 100644
index 0000000..d0d0573
--- /dev/null
+++ b/tests/iconifydelay.c
@@ -0,0 +1,67 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ iconifydelay.c for the Openbox window manager
+ Copyright (c) 2009 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ XEvent msg;
+ int x=50,y=50,h=100,w=400;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ win = XCreateWindow(display, RootWindow(display, DefaultScreen(display)),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+ XSetWindowBackground(display, win,
+ WhitePixel(display, DefaultScreen(display)));
+
+ usleep(1000000);
+ XMapWindow(display, win);
+ XFlush(display);
+ usleep(1000000);
+
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = XInternAtom(display, "WM_CHANGE_STATE", False);
+ msg.xclient.display = display;
+ msg.xclient.window = win;
+ msg.xclient.format = 32;
+ msg.xclient.data.l[0] = IconicState;
+ msg.xclient.data.l[1] = 0;
+ msg.xclient.data.l[2] = 0;
+ msg.xclient.data.l[3] = 0;
+ msg.xclient.data.l[4] = 0;
+ XSendEvent(display, RootWindow(display, DefaultScreen(display)),
+ False, SubstructureNotifyMask|SubstructureRedirectMask, &msg);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/icons.c b/tests/icons.c
new file mode 100644
index 0000000..e2477c2
--- /dev/null
+++ b/tests/icons.c
@@ -0,0 +1,251 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ icons.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <glib.h>
+
+Window findClient(Display *d, Window win)
+{
+ Window r, *children;
+ unsigned int n, i;
+ Atom state = XInternAtom(d, "WM_STATE", True);
+ Atom ret_type;
+ int ret_format;
+ unsigned long ret_items, ret_bytesleft;
+ unsigned long *prop_return;
+
+ XQueryTree(d, win, &r, &r, &children, &n);
+ for (i = 0; i < n; ++i) {
+ Window w = findClient(d, children[i]);
+ if (w) return w;
+ }
+
+ // try me
+ XGetWindowProperty(d, win, state, 0, 1,
+ False, state, &ret_type, &ret_format,
+ &ret_items, &ret_bytesleft,
+ (unsigned char**) &prop_return);
+ if (ret_type == None || ret_items < 1)
+ return None;
+ return win; // found it!
+}
+
+int main(int argc, char **argv)
+{
+ Display *d = XOpenDisplay(NULL);
+ int s = DefaultScreen(d);
+ Atom net_wm_icon = XInternAtom(d, "_NET_WM_ICON", True);
+ Atom ret_type;
+ unsigned int winw = 0, winh = 0;
+ int ret_format;
+ unsigned long ret_items, ret_bytesleft;
+ const int MAX_IMAGES = 10;
+ unsigned long *prop_return[MAX_IMAGES];
+ XImage *i[MAX_IMAGES];
+ long offset = 0;
+ unsigned int image = 0;
+ unsigned int j; // loop counter
+ Window id, win;
+ Pixmap p;
+ Cursor cur;
+ XEvent ev;
+ unsigned int bs = sizeof(long);
+
+ printf("Click on a window with an icon...\n");
+
+ //int id = strtol(argv[1], NULL, 16);
+ XUngrabPointer(d, CurrentTime);
+ cur = XCreateFontCursor(d, XC_crosshair);
+ XGrabPointer(d, RootWindow(d, s), False, ButtonPressMask, GrabModeAsync,
+ GrabModeAsync, None, cur, CurrentTime);
+ while (1) {
+ XNextEvent(d, &ev);
+ if (ev.type == ButtonPress) {
+ XUngrabPointer(d, CurrentTime);
+ id = findClient(d, ev.xbutton.subwindow);
+ break;
+ }
+ }
+
+ printf("Using window 0x%lx\n", id);
+
+ do {
+ unsigned int w, h;
+
+ XGetWindowProperty(d, id, net_wm_icon, offset++, 1,
+ False, XA_CARDINAL, &ret_type, &ret_format,
+ &ret_items, &ret_bytesleft,
+ (unsigned char**) &prop_return[image]);
+ if (ret_type == None || ret_items < 1) {
+ printf("No icon found\n");
+ return 1;
+ }
+ w = prop_return[image][0];
+ XFree(prop_return[image]);
+
+ XGetWindowProperty(d, id, net_wm_icon, offset++, 1,
+ False, XA_CARDINAL, &ret_type, &ret_format,
+ &ret_items, &ret_bytesleft,
+ (unsigned char**) &prop_return[image]);
+ if (ret_type == None || ret_items < 1) {
+ printf("Failed to get height\n");
+ return 1;
+ }
+ h = prop_return[image][0];
+ XFree(prop_return[image]);
+
+ XGetWindowProperty(d, id, net_wm_icon, offset, w*h,
+ False, XA_CARDINAL, &ret_type, &ret_format,
+ &ret_items, &ret_bytesleft,
+ (unsigned char**) &prop_return[image]);
+ if (ret_type == None || ret_items < w*h) {
+ printf("Failed to get image data\n");
+ return 1;
+ }
+ offset += w*h;
+
+ printf("Found icon with size %dx%d\n", w, h);
+
+ i[image] = XCreateImage(d, DefaultVisual(d, s), DefaultDepth(d, s),
+ ZPixmap, 0, NULL, w, h, 32, 0);
+ assert(i[image]);
+ i[image]->byte_order = LSBFirst;
+ i[image]->data = (char*)prop_return[image];
+ for (j = 0; j < w*h; j++) {
+ unsigned char alpha = (unsigned char)i[image]->data[j*bs+3];
+ unsigned char r = (unsigned char) i[image]->data[j*bs+0];
+ unsigned char g = (unsigned char) i[image]->data[j*bs+1];
+ unsigned char b = (unsigned char) i[image]->data[j*bs+2];
+
+ // background color
+ unsigned char bgr = 0;
+ unsigned char bgg = 0;
+ unsigned char bgb = 0;
+
+ r = bgr + (r - bgr) * alpha / 256;
+ g = bgg + (g - bgg) * alpha / 256;
+ b = bgb + (b - bgb) * alpha / 256;
+
+ i[image]->data[j*4+0] = (char) r;
+ i[image]->data[j*4+1] = (char) g;
+ i[image]->data[j*4+2] = (char) b;
+ }
+
+ winw += w;
+ if (h > winh) winh = h;
+
+ ++image;
+ } while (ret_bytesleft > 0 && image < MAX_IMAGES);
+
+#define hashsize(n) ((guint32)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+ /* hash the images */
+ for (j = 0; j < image; ++j) {
+ unsigned int w, h, length;
+ guint32 a,b,c;
+ guint32 initval = 0xf00d;
+ const guint32 *k = (guint32*)i[j]->data;
+
+ w = i[j]->width;
+ h = i[j]->height;
+ length = w * h;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + (((guint32)length)<<2) + initval;
+
+ /*---------------------------------------- handle most of the key */
+ while (length > 3)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 3;
+ k += 3;
+ }
+
+ /*--------------------------------- handle the last 3 uint32_t's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3 : c+=k[2];
+ case 2 : b+=k[1];
+ case 1 : a+=k[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /*------------------------------------ report the result */
+ printf("image[%d] %ux%u %lu\n", j, w, h, c);
+ }
+
+ win = XCreateSimpleWindow(d, RootWindow(d, s), 0, 0, winw, winh,
+ 0, 0, 0);
+ assert(win);
+ XMapWindow(d, win);
+
+ p = XCreatePixmap(d, win, winw, winh, DefaultDepth(d, s));
+ XFillRectangle(d, p, DefaultGC(d, s), 0, 0, winw, winh);
+
+ for (j = 0; j < image; ++j) {
+ static unsigned int x = 0;
+
+ XPutImage(d, p, DefaultGC(d, s), i[j], 0, 0, x, 0,
+ i[j]->width, i[j]->height);
+ x += i[j]->width;
+ XDestroyImage(i[j]);
+ }
+
+ XSetWindowBackgroundPixmap(d, win, p);
+ XClearWindow(d, win);
+
+ XFlush(d);
+
+ getchar();
+
+ XFreePixmap(d, p);
+ XCloseDisplay(d);
+}
diff --git a/tests/mapiconic.c b/tests/mapiconic.c
new file mode 100644
index 0000000..76455a0
--- /dev/null
+++ b/tests/mapiconic.c
@@ -0,0 +1,80 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ urgent.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ Atom wm_state;
+ XEvent msg;
+ int x=50,y=50,h=100,w=400;
+ unsigned long state[2];
+ XWMHints *hints;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ state[0] = IconicState;
+ state[1] = None;
+
+ wm_state = XInternAtom(display, "WM_STATE", False);
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ hints = XAllocWMHints();
+ hints->flags = StateHint;
+ hints->initial_state = IconicState;
+ XSetWMHints(display, win, hints);
+ XFree(hints);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/mingrow.c b/tests/mingrow.c
new file mode 100644
index 0000000..b132fa1
--- /dev/null
+++ b/tests/mingrow.c
@@ -0,0 +1,100 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ noresize.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main () {
+ XSetWindowAttributes xswa;
+ unsigned long xswamask;
+ Display *display;
+ Window win;
+ XEvent report;
+ int x=10,y=10,h=100,w=100;
+ XSizeHints size;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ xswa.win_gravity = StaticGravity;
+ xswamask = CWWinGravity;
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 0, CopyFromParent, CopyFromParent,
+ CopyFromParent, xswamask, &xswa);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ size.flags = PMinSize | PMaxSize;
+ size.max_width = 0;
+ size.min_width = w;
+ size.max_height = 0;
+ size.min_height = h;
+ XSetWMNormalHints(display, win, &size);
+
+ XSelectInput(display, win, ExposureMask | StructureNotifyMask);
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ printf("sleeping 4..");
+ fflush(stdout);
+ sleep(1);
+ printf("3..");
+ fflush(stdout);
+ sleep(1);
+ printf("2..");
+ fflush(stdout);
+ sleep(1);
+ printf("1..");
+ fflush(stdout);
+ sleep(1);
+ printf("\n");
+
+ size.flags = PMinSize | PMaxSize;
+ size.max_width = 0;
+ size.min_width = w*2;
+ size.max_height = 0;
+ size.min_height = h*2;
+ XSetWMNormalHints(display, win, &size);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/modal.c b/tests/modal.c
new file mode 100644
index 0000000..edbd5cc
--- /dev/null
+++ b/tests/modal.c
@@ -0,0 +1,64 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ modal.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window parent, child;
+ XEvent report;
+ Atom state, modal;
+ int x=10,y=10,h=400,w=400;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ state = XInternAtom(display, "_NET_WM_STATE", True);
+ modal = XInternAtom(display, "_NET_WM_STATE_MODAL", True);
+
+ parent = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ child = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w/2, h/2, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ XSetWindowBackground(display,parent,WhitePixel(display,0));
+ XSetWindowBackground(display,child,BlackPixel(display,0));
+
+ XSetTransientForHint(display, child, parent);
+ XChangeProperty(display, child, state, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&modal, 1);
+
+ XMapWindow(display, parent);
+ XMapWindow(display, child);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/modal2.c b/tests/modal2.c
new file mode 100644
index 0000000..e7afb9b
--- /dev/null
+++ b/tests/modal2.c
@@ -0,0 +1,74 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ modal2.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window parent, child;
+ XEvent report;
+ Atom state, modal;
+ int x=10,y=10,h=400,w=400;
+ XEvent ce;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ state = XInternAtom(display, "_NET_WM_STATE", True);
+ modal = XInternAtom(display, "_NET_WM_STATE_MODAL", True);
+
+ parent = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ child = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w/2, h/2, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ XSetWindowBackground(display,parent,WhitePixel(display,0));
+ XSetWindowBackground(display,child,BlackPixel(display,0));
+
+ XSetTransientForHint(display, child, parent);
+
+ XMapWindow(display, parent);
+ XMapWindow(display, child);
+ XFlush(display);
+
+ ce.xclient.type = ClientMessage;
+ ce.xclient.message_type = state;
+ ce.xclient.display = display;
+ ce.xclient.window = child;
+ ce.xclient.format = 32;
+ ce.xclient.data.l[0] = 1;
+ ce.xclient.data.l[1] = modal;
+ ce.xclient.data.l[2] = 0;
+ XSendEvent(display, RootWindow(display, DefaultScreen(display)),
+ False, SubstructureNotifyMask | SubstructureRedirectMask, &ce);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/modal3.c b/tests/modal3.c
new file mode 100644
index 0000000..76c8219
--- /dev/null
+++ b/tests/modal3.c
@@ -0,0 +1,78 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ modal3.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window parent, child;
+ XEvent report;
+ Atom state, modal;
+ int x=10,y=10,h=400,w=400;
+ XEvent ce;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ state = XInternAtom(display, "_NET_WM_STATE", True);
+ modal = XInternAtom(display, "_NET_WM_STATE_MODAL", True);
+
+ parent = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ child = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w/2, h/2, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ XSetWindowBackground(display,parent,WhitePixel(display,0));
+ XSetWindowBackground(display,child,BlackPixel(display,0));
+
+ XSetTransientForHint(display, child, parent);
+
+ XMapWindow(display, parent);
+ XMapWindow(display, child);
+ XFlush(display);
+
+ ce.xclient.type = ClientMessage;
+ ce.xclient.message_type = state;
+ ce.xclient.display = display;
+ ce.xclient.window = child;
+ ce.xclient.format = 32;
+ ce.xclient.data.l[0] = 1;
+ ce.xclient.data.l[1] = modal;
+ ce.xclient.data.l[2] = 0;
+ XSendEvent(display, RootWindow(display, DefaultScreen(display)),
+ False, SubstructureNotifyMask | SubstructureRedirectMask, &ce);
+
+ ce.xclient.data.l[0] = 0;
+ XSendEvent(display, RootWindow(display, DefaultScreen(display)),
+ False, SubstructureNotifyMask | SubstructureRedirectMask, &ce);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/noresize.c b/tests/noresize.c
new file mode 100644
index 0000000..c98295b
--- /dev/null
+++ b/tests/noresize.c
@@ -0,0 +1,82 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ noresize.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main () {
+ XSetWindowAttributes xswa;
+ unsigned long xswamask;
+ Display *display;
+ Window win;
+ XEvent report;
+ int x=10,y=10,h=100,w=400;
+ XSizeHints size;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ xswa.win_gravity = StaticGravity;
+ xswamask = CWWinGravity;
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 0, CopyFromParent, CopyFromParent,
+ CopyFromParent, xswamask, &xswa);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ size.flags = PMinSize | PMaxSize;
+ size.max_width = 0;
+ size.min_width = w;
+ size.max_height = 0;
+ size.min_height = h;
+ XSetWMNormalHints(display, win, &size);
+
+ XSelectInput(display, win, ExposureMask | StructureNotifyMask);
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ XMoveWindow(display, win, 10, 10);
+ XMoveWindow(display, win, 10, 10);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/oldfullscreen.c b/tests/oldfullscreen.c
new file mode 100644
index 0000000..543e960
--- /dev/null
+++ b/tests/oldfullscreen.c
@@ -0,0 +1,94 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ oldfullscreen.c for the Openbox window manager
+ Copyright (c) 2010 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+typedef struct
+{
+ unsigned long flags;
+ unsigned long functions;
+ unsigned long decorations;
+ long inputMode;
+ unsigned long status;
+} Hints;
+
+int main (int argc, char **argv) {
+ Display *display;
+ Window win;
+ Window r;
+ XEvent report;
+ int x=200,y=200,h=100,w=400,s;
+ XSizeHints *size;
+ Hints hints;
+ Atom prop;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ XGetGeometry(display, RootWindow(display, DefaultScreen(display)), &r,
+ &x, &y, &w, &h, &s, &s);
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 0, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ size = XAllocSizeHints();
+ size->flags = PPosition;
+ XSetWMNormalHints(display,win,size);
+ XFree(size);
+
+ hints.flags = 2;
+ hints.decorations = 0;
+ prop = XInternAtom(display, "_MOTIF_WM_HINTS", False);
+ XChangeProperty(display, win, prop, prop, 32, PropModeReplace,
+ (unsigned char *)&hints, 5);
+
+ XFlush(display);
+ XMapWindow(display, win);
+
+ XSelectInput(display, win, StructureNotifyMask | ButtonPressMask);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case ButtonPress:
+ XUnmapWindow(display, win);
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ s = report.xconfigure.send_event;
+ printf("confignotify %i,%i-%ix%i (send: %d)\n",x,y,w,h,s);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/override.c b/tests/override.c
new file mode 100644
index 0000000..44c3a00
--- /dev/null
+++ b/tests/override.c
@@ -0,0 +1,76 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ override.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+
+int main () {
+ XSetWindowAttributes xswa;
+ unsigned long xswamask;
+ Display *display;
+ Window win;
+ XEvent report;
+ int x=10,y=10,h=100,w=400;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ xswa.override_redirect = True;
+ xswamask = CWOverrideRedirect;
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, xswamask, &xswa);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+ sleep(1);
+ XUnmapWindow(display, win);
+ XFlush(display);
+ sleep(1);
+ XMapWindow(display, win);
+ XFlush(display);
+
+ XSelectInput(display, win, ExposureMask | StructureNotifyMask);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/overrideinputonly.c b/tests/overrideinputonly.c
new file mode 100644
index 0000000..0c13ac3
--- /dev/null
+++ b/tests/overrideinputonly.c
@@ -0,0 +1,58 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ override.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+
+int main (int argc, char *argv[]) {
+ XSetWindowAttributes xswa;
+ unsigned long xswamask;
+ Display *display;
+ Window win;
+ XEvent report;
+ int i,x=0,y=0,h=1,w=1;
+
+ for (i=0; i < argc; i++) {
+ if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "-geometry")) {
+ XParseGeometry(argv[++i], &x, &y, &w, &h);
+ }
+ }
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ xswa.override_redirect = True;
+ xswamask = CWOverrideRedirect;
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 0, 0, InputOnly,
+ CopyFromParent, xswamask, &xswa);
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/positioned.c b/tests/positioned.c
new file mode 100644
index 0000000..26f835d
--- /dev/null
+++ b/tests/positioned.c
@@ -0,0 +1,73 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ positioned.c for the Openbox window manager
+ Copyright (c) 2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main (int argc, char **argv) {
+ Display *display;
+ Window win;
+ XEvent report;
+ int x=200,y=200,h=100,w=400,s;
+ XSizeHints *size;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 0, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ size = XAllocSizeHints();
+ size->flags = PPosition;
+ XSetWMNormalHints(display,win,size);
+ XFree(size);
+
+ XFlush(display);
+ XMapWindow(display, win);
+
+ XSelectInput(display, win, StructureNotifyMask | ButtonPressMask);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case ButtonPress:
+ XUnmapWindow(display, win);
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ s = report.xconfigure.send_event;
+ printf("confignotify %i,%i-%ix%i (send: %d)\n",x,y,w,h,s);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/resize.c b/tests/resize.c
new file mode 100644
index 0000000..e3a84bb
--- /dev/null
+++ b/tests/resize.c
@@ -0,0 +1,69 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ resize.c for the Openbox window manager
+ Copyright (c) 2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ int x=10,y=10,h=100,w=400;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ sleep(5);
+ XResizeWindow(display, win, 600, 150);
+
+ XSelectInput(display, win, ExposureMask | StructureNotifyMask);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/restack.c b/tests/restack.c
new file mode 100644
index 0000000..fe1bcdb
--- /dev/null
+++ b/tests/restack.c
@@ -0,0 +1,139 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ extentsrequest.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ Atom _restack;
+ XEvent msg;
+ int x=10,y=10,h=100,w=400;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ _restack = XInternAtom(display, "_NET_RESTACK_WINDOW", False);
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ printf("requesting bottom in 3\n");
+ sleep(3);
+
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = _restack;
+ msg.xclient.display = display;
+ msg.xclient.window = win;
+ msg.xclient.format = 32;
+ msg.xclient.data.l[0] = 2l;
+ msg.xclient.data.l[1] = 0l;
+ msg.xclient.data.l[2] = Below;
+ msg.xclient.data.l[3] = 0l;
+ msg.xclient.data.l[4] = 0l;
+ XSendEvent(display, RootWindow(display, 0), False,
+ SubstructureNotifyMask | SubstructureRedirectMask, &msg);
+ XFlush(display);
+
+ printf("requesting top in 3\n");
+ sleep(3);
+
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = _restack;
+ msg.xclient.display = display;
+ msg.xclient.window = win;
+ msg.xclient.format = 32;
+ msg.xclient.data.l[0] = 2l;
+ msg.xclient.data.l[1] = 0l;
+ msg.xclient.data.l[2] = Above;
+ msg.xclient.data.l[3] = 0l;
+ msg.xclient.data.l[4] = 0l;
+ XSendEvent(display, RootWindow(display, 0), False,
+ SubstructureNotifyMask | SubstructureRedirectMask, &msg);
+ XFlush(display);
+
+ printf("requesting bottomif in 3\n");
+ sleep(3);
+
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = _restack;
+ msg.xclient.display = display;
+ msg.xclient.window = win;
+ msg.xclient.format = 32;
+ msg.xclient.data.l[0] = 2l;
+ msg.xclient.data.l[1] = 0l;
+ msg.xclient.data.l[2] = BottomIf;
+ msg.xclient.data.l[3] = 0l;
+ msg.xclient.data.l[4] = 0l;
+ XSendEvent(display, RootWindow(display, 0), False,
+ SubstructureNotifyMask | SubstructureRedirectMask, &msg);
+ XFlush(display);
+
+ printf("requesting topif in 3\n");
+ sleep(3);
+
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = _restack;
+ msg.xclient.display = display;
+ msg.xclient.window = win;
+ msg.xclient.format = 32;
+ msg.xclient.data.l[0] = 2l;
+ msg.xclient.data.l[1] = 0l;
+ msg.xclient.data.l[2] = TopIf;
+ msg.xclient.data.l[3] = 0l;
+ msg.xclient.data.l[4] = 0l;
+ XSendEvent(display, RootWindow(display, 0), False,
+ SubstructureNotifyMask | SubstructureRedirectMask, &msg);
+ XFlush(display);
+
+ printf("requesting opposite in 3\n");
+ sleep(3);
+
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = _restack;
+ msg.xclient.display = display;
+ msg.xclient.window = win;
+ msg.xclient.format = 32;
+ msg.xclient.data.l[0] = 2l;
+ msg.xclient.data.l[1] = 0l;
+ msg.xclient.data.l[2] = Opposite;
+ msg.xclient.data.l[3] = 0l;
+ msg.xclient.data.l[4] = 0l;
+ XSendEvent(display, RootWindow(display, 0), False,
+ SubstructureNotifyMask | SubstructureRedirectMask, &msg);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/shape.c b/tests/shape.c
new file mode 100644
index 0000000..c3de7da
--- /dev/null
+++ b/tests/shape.c
@@ -0,0 +1,77 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ shape.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/shape.h>
+
+int main () {
+
+ Display *display;
+ Window win;
+ XEvent report;
+ XEvent msg;
+ int x=50,y=50,h=100,w=400;
+ XWMHints hint;
+ XRectangle xrect;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+ xrect.x = 10;
+ xrect.y = 10;
+ xrect.width = w - 20;
+ xrect.height = h - 20;
+ XShapeCombineRectangles(display, win,
+ ShapeBounding, 0, 0, &xrect, 1,
+ ShapeSet, Unsorted);
+
+ XSetWindowBackground(display,win,BlackPixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/showhide.c b/tests/showhide.c
new file mode 100644
index 0000000..d1a63e2
--- /dev/null
+++ b/tests/showhide.c
@@ -0,0 +1,54 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ showhide.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ XEvent msg;
+ int x=50,y=50,h=100,w=400;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ while (1) {
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+ XMapWindow(display, win);
+ XFlush(display);
+ usleep(1000);
+ XDestroyWindow(display, win);
+ XSync(display, False);
+
+ break;
+ sleep(2);
+ }
+
+ return 1;
+}
diff --git a/tests/skiptaskbar.c b/tests/skiptaskbar.c
new file mode 100644
index 0000000..bb585c6
--- /dev/null
+++ b/tests/skiptaskbar.c
@@ -0,0 +1,63 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ skiptaskbar.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ Atom state, skip;
+ XClassHint classhint;
+ int x=10,y=10,h=400,w=400;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ state = XInternAtom(display, "_NET_WM_STATE", True);
+ skip = XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", True);
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XChangeProperty(display, win, state, XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&skip, 1);
+
+ classhint.res_name = "test";
+ classhint.res_class = "Test";
+ XSetClassHint(display, win, &classhint);
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/skiptaskbar2.c b/tests/skiptaskbar2.c
new file mode 100644
index 0000000..3fb2231
--- /dev/null
+++ b/tests/skiptaskbar2.c
@@ -0,0 +1,68 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ skiptaskbar2.c for the Openbox window manager
+ Copyright (c) 2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report, ce;
+ Atom state, skip;
+ int x=10,y=10,h=400,w=400;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ state = XInternAtom(display, "_NET_WM_STATE", True);
+ skip = XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", True);
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ sleep(1);
+
+ ce.xclient.type = ClientMessage;
+ ce.xclient.message_type = state;
+ ce.xclient.display = display;
+ ce.xclient.window = win;
+ ce.xclient.format = 32;
+ ce.xclient.data.l[0] = 1;
+ ce.xclient.data.l[1] = skip;
+ ce.xclient.data.l[2] = 0;
+ XSendEvent(display, RootWindow(display, DefaultScreen(display)),
+ False, SubstructureNotifyMask | SubstructureRedirectMask, &ce);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/stackabove.c b/tests/stackabove.c
new file mode 100644
index 0000000..791690f
--- /dev/null
+++ b/tests/stackabove.c
@@ -0,0 +1,73 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ extentsrequest.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+/* Will find the frame for this window and stack itself right above it */
+#define GO_ABOVE 0x4c0003c
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window win, frame, a, p, *c;
+ unsigned int n;
+ XWindowChanges changes;
+ XEvent report;
+ XEvent msg;
+ int x=10,y=10,h=100,w=400;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ printf("requesting move in 10\n");
+ sleep(10);
+
+ frame = win;
+ while (XQueryTree(display, frame, &a, &p, &c, &n) && p != a)
+ frame = p;
+
+ changes.sibling = GO_ABOVE;
+ while (XQueryTree(display, changes.sibling, &a, &p, &c, &n) && p != a)
+ changes.sibling = p;
+
+ changes.stack_mode = Above;
+ XConfigureWindow(display, frame, CWSibling | CWStackMode, &changes);
+ XFlush(display);
+
+ printf("moved 0x%lx above 0x%lx\n", frame, changes.sibling);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/stacking.c b/tests/stacking.c
new file mode 100644
index 0000000..4d2bda9
--- /dev/null
+++ b/tests/stacking.c
@@ -0,0 +1,80 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ stacking.c for the Openbox window manager
+ Copyright (c) 2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window parent, child1, child2, group;
+ XEvent report;
+ int x=10,y=10,h=400,w=400;
+ XWMHints *hints;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ group = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ parent = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ child1 = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w/2, h/2, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ child2 = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w/2, h/2, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ XSetWindowBackground(display,parent,WhitePixel(display,0));
+ XSetWindowBackground(display,child1,BlackPixel(display,0));
+ XSetWindowBackground(display,child2,WhitePixel(display,0));
+
+ hints = XAllocWMHints();
+ hints->flags = WindowGroupHint;
+ hints->window_group = group;
+ XSetWMHints(display, parent, hints);
+ XSetWMHints(display, child1, hints);
+ XSetWMHints(display, child2, hints);
+ XFree(hints);
+
+ XSetTransientForHint(display, child1,
+ RootWindow(display, DefaultScreen(display)));
+ XSetTransientForHint(display, child2, parent);
+
+ XMapWindow(display, parent);
+ XFlush(display);
+ sleep(3);
+ XMapWindow(display, child1);
+ XFlush(display);
+ sleep(3);
+ XMapWindow(display, child2);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/strut.c b/tests/strut.c
new file mode 100644
index 0000000..15a4860
--- /dev/null
+++ b/tests/strut.c
@@ -0,0 +1,84 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ strut.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ Atom _net_strut;
+ XEvent msg;
+ int x=10,y=10,h=100,w=400;
+ int s[4];
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ _net_strut = XInternAtom(display, "_NET_WM_STRUT", False);
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+ sleep(2);
+
+ printf("top\n");
+ s[0] = 0; s[1] = 0; s[2] = 20; s[3] = 0;
+ XChangeProperty(display, win, _net_strut, XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char*) s, 4);
+ XFlush(display);
+ sleep(2);
+
+ printf("none\n");
+ XDeleteProperty(display, win, _net_strut);
+ XFlush(display);
+
+ XSelectInput(display, win, ExposureMask | StructureNotifyMask);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/title.c b/tests/title.c
new file mode 100644
index 0000000..799bdf5
--- /dev/null
+++ b/tests/title.c
@@ -0,0 +1,86 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ title.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main (int argc, char **argv) {
+ Display *display;
+ Window win;
+ XEvent report;
+ int x=10,y=10,h=100,w=400;
+ XSizeHints size;
+ XTextProperty name;
+ Atom nameprop,nameenc;
+
+ if (argc < 2) return 1;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ if (argc > 2)
+ nameprop = XInternAtom(display,argv[2],False);
+ else
+ nameprop = XInternAtom(display,"WM_NAME",False);
+ if (argc > 3)
+ nameenc = XInternAtom(display,argv[3],False);
+ else
+ nameenc = XInternAtom(display,"STRING",False);
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+// XStringListToTextProperty(&argv[1], 1, &name);
+// XSetWMName(display, win, &name);
+ XChangeProperty(display, win, nameprop, nameenc, 8,
+ PropModeAppend, argv[1], strlen(argv[1]));
+
+ XFlush(display);
+ XMapWindow(display, win);
+
+ XSelectInput(display, win, ExposureMask | StructureNotifyMask);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/urgent.c b/tests/urgent.c
new file mode 100644
index 0000000..c29e300
--- /dev/null
+++ b/tests/urgent.c
@@ -0,0 +1,77 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ urgent.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+int main () {
+ Display *display;
+ Window win;
+ XEvent report;
+ Atom _net_fs, _net_state;
+ XEvent msg;
+ int x=50,y=50,h=100,w=400;
+ XWMHints hint;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ _net_state = XInternAtom(display, "_NET_WM_STATE", False);
+ _net_fs = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False);
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+ sleep(1);
+
+ printf("urgent on\n");
+ hint.flags = XUrgencyHint;
+ XSetWMHints(display, win, &hint);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case Expose:
+ printf("exposed\n");
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ printf("confignotify %i,%i-%ix%i\n",x,y,w,h);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/tests/usertimewin.c b/tests/usertimewin.c
new file mode 100644
index 0000000..7a2aa97
--- /dev/null
+++ b/tests/usertimewin.c
@@ -0,0 +1,74 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ usertimewin.c for the Openbox window manager
+ Copyright (c) 2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+int main () {
+ Display *display;
+ Window win, twin;
+ XEvent report;
+ Atom atime, atimewin;
+ int x=10,y=10,h=400,w=400;
+ Time num;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ atime = XInternAtom(display, "_NET_WM_USER_TIME", True);
+ atimewin = XInternAtom(display, "_NET_WM_USER_TIME_WINDOW", True);
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+ twin = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w/2, h/2, 10, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, 0);
+
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ sleep(2);
+
+ printf("Setting time window\n");
+ XChangeProperty(display, win, atimewin, XA_WINDOW, 32,
+ PropModeReplace, (unsigned char*)&twin, 1);
+ XFlush(display);
+
+ sleep(1);
+
+ num = 100;
+ printf("Setting time stamp on time window\n");
+ XChangeProperty(display, twin, atime, XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char*)&num, 1);
+ XFlush(display);
+
+ while (1) {
+ XNextEvent(display, &report);
+ }
+
+ return 1;
+}
diff --git a/tests/wmhints.c b/tests/wmhints.c
new file mode 100644
index 0000000..b8ab57b
--- /dev/null
+++ b/tests/wmhints.c
@@ -0,0 +1,97 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ oldfullscreen.c for the Openbox window manager
+ Copyright (c) 2010 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+typedef struct
+{
+ unsigned long flags;
+ unsigned long functions;
+ unsigned long decorations;
+ long inputMode;
+ unsigned long status;
+} Hints;
+
+int main (int argc, char **argv) {
+ Display *display;
+ Window win;
+ Window r;
+ XEvent report;
+ int x=200,y=200,h=100,w=400,s;
+ Hints hints;
+ Atom prop;
+
+ display = XOpenDisplay(NULL);
+
+ if (display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+ win = XCreateWindow(display, RootWindow(display, 0),
+ x, y, w, h, 0, CopyFromParent, CopyFromParent,
+ CopyFromParent, 0, NULL);
+ XSetWindowBackground(display,win,WhitePixel(display,0));
+
+ hints.flags = 2;
+ hints.decorations = 0;
+ prop = XInternAtom(display, "_MOTIF_WM_HINTS", False);
+ XChangeProperty(display, win, prop, prop, 32, PropModeReplace,
+ (unsigned char *)&hints, 5);
+
+ XSelectInput(display, win, StructureNotifyMask | ButtonPressMask);
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+ sleep(1);
+
+ hints.flags = 2;
+ hints.decorations = 1<<3 || 1 << 1; /* border and title */
+ prop = XInternAtom(display, "_MOTIF_WM_HINTS", False);
+ XChangeProperty(display, win, prop, prop, 32, PropModeReplace,
+ (unsigned char *)&hints, 5);
+
+ XMapWindow(display, win);
+ XFlush(display);
+
+
+ while (1) {
+ XNextEvent(display, &report);
+
+ switch (report.type) {
+ case ButtonPress:
+ XUnmapWindow(display, win);
+ break;
+ case ConfigureNotify:
+ x = report.xconfigure.x;
+ y = report.xconfigure.y;
+ w = report.xconfigure.width;
+ h = report.xconfigure.height;
+ s = report.xconfigure.send_event;
+ printf("confignotify %i,%i-%ix%i (send: %d)\n",x,y,w,h,s);
+ break;
+ }
+
+ }
+
+ return 1;
+}
diff --git a/themes/Artwiz-boxed/openbox-3/themerc b/themes/Artwiz-boxed/openbox-3/themerc
new file mode 100644
index 0000000..890c98d
--- /dev/null
+++ b/themes/Artwiz-boxed/openbox-3/themerc
@@ -0,0 +1,102 @@
+menu.title.bg: raised gradient diagonal
+menu.title.bg.color: rgb:90/94/98
+menu.title.bg.colorTo: rgb:20/24/28
+menu.title.text.color: white
+menu.title.text.justify: center
+
+menu.items.bg: sunken gradient diagonal
+menu.items.bg.color: rgb:10/20/30
+menu.items.bg.colorTo: rgb:70/80/90
+menu.items.text.color: rgb:90/a0/b0
+menu.items.justify: center
+
+menu.items.active.bg: raised gradient diagonal
+menu.items.active.bg.color: rgb:90/94/98
+menu.items.active.bg.colorTo: rgb:20/24/28
+menu.items.active.text.color: white
+menu.bullet.image.color: rgb:90/a0/b0
+menu.bullet.selected.image.color: #ffffff
+
+window.active.title.bg: raised gradient vertical
+window.active.title.bg.color: rgb:80/84/88
+window.active.title.bg.colorTo: rgb:30/34/38
+window.inactive.title.bg: raised vertical gradient
+window.inactive.title.bg.color: rgb:50/54/58
+window.inactive.title.bg.colorTo: black
+
+window.active.label.bg: sunken diagonal gradient
+window.active.label.bg.color: rgb:10/20/30
+window.active.label.bg.colorTo: rgb:70/80/90
+window.active.label.text.color: white
+window.inactive.label.bg: sunken gradient diagonal
+window.inactive.label.bg.color: black
+window.inactive.label.bg.colorTo: rgb:40/50/60
+window.inactive.label.text.color: rgb:60/64/68
+window.label.text.justify: center
+
+window.active.button.unpressed.bg: raised gradient diagonal
+window.active.button.unpressed.bg.color: rgb:90/94/98
+window.active.button.unpressed.bg.colorTo: rgb:20/24/28
+window.active.button.unpressed.image.color: white
+
+window.inactive.button.unpressed.bg: raised gradient diagonal
+window.inactive.button.unpressed.bg.color: rgb:50/54/58
+window.inactive.button.unpressed.bg.colorTo: black
+window.inactive.button.unpressed.image.color: rgb:70/74/78
+
+window.active.button.pressed.bg: sunken gradient diagonal
+window.active.button.pressed.bg.color: rgb:20/40/50
+window.active.button.pressed.bg.colorTo: rgb:60/70/80
+
+window.inactive.button.pressed.bg: sunken gradient diagonal
+window.inactive.button.pressed.bg.color: rgb:50/54/58
+window.inactive.button.pressed.bg.colorTo: black
+window.inactive.button.pressed.image.color: rgb:70/74/78
+
+window.active.client.color: rgb:40/44/48
+window.inactive.client.color: rgb:20/24/28
+
+window.active.handle.bg: raised gradient diagonal
+window.active.handle.bg.color: rgb:70/74/78
+window.active.handle.bg.colorTo: rgb:40/44/48
+
+window.inactive.handle.bg: raised gradient diagonal
+window.inactive.handle.bg.color: rgb:50/54/58
+window.inactive.handle.bg.colorTo: black
+
+window.active.grip.bg: sunken diagonal gradient
+window.active.grip.bg.color: rgb:20/30/40
+window.active.grip.bg.colorTo: rgb:60/70/80
+
+window.inactive.grip.bg: sunken diagonal gradient
+window.inactive.grip.bg.color: black
+window.inactive.grip.bg.colorTo: rgb:30/40/50
+
+window.active.button.toggled.bg: raised gradient diagonal
+window.active.button.toggled.bg.color: rgb:90/94/98
+window.active.button.toggled.bg.colorTo: rgb:20/24/28
+window.active.button.toggled.image.color: white
+
+window.inactive.button.toggled.bg: raised gradient diagonal
+window.inactive.button.toggled.bg.color: rgb:50/54/58
+window.inactive.button.toggled.bg.colorTo: black
+window.inactive.button.toggled.image.color: rgb:70/74/78
+
+window.active.button.disabled.bg: raised gradient diagonal
+window.active.button.disabled.bg.color: rgb:90/94/98
+window.active.button.disabled.bg.colorTo: rgb:20/24/28
+window.active.button.disabled.image.color: grey
+
+window.inactive.button.disabled.bg: raised gradient diagonal
+window.inactive.button.disabled.bg.color: rgb:50/54/58
+window.inactive.button.disabled.bg.colorTo: black
+window.inactive.button.disabled.image.color: rgb:70/74/78
+
+border.color: black
+padding.width: 1
+borderWidth: 1
+window.handle.width: 4
+
+window.active.label.text.font:
+menu.title.text.font:
+menu.items.font:
diff --git a/themes/Bear2/openbox-3/close.xbm b/themes/Bear2/openbox-3/close.xbm
new file mode 100644
index 0000000..ab7ff80
--- /dev/null
+++ b/themes/Bear2/openbox-3/close.xbm
@@ -0,0 +1,4 @@
+#define close_width 8
+#define close_height 8
+static unsigned char close_bits[] = {
+ 0xc3, 0xe7, 0x7e, 0x3c, 0x3c, 0x7e, 0xe7, 0xc3 };
diff --git a/themes/Bear2/openbox-3/close_pressed.xbm b/themes/Bear2/openbox-3/close_pressed.xbm
new file mode 100644
index 0000000..663c458
--- /dev/null
+++ b/themes/Bear2/openbox-3/close_pressed.xbm
@@ -0,0 +1,5 @@
+#define close_pressed_width 10
+#define close_pressed_height 10
+static unsigned char close_pressed_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x0c, 0x03, 0x9c, 0x03, 0xf8, 0x01, 0xf0, 0x00,
+ 0xf0, 0x00, 0xf8, 0x01, 0x9c, 0x03, 0x0c, 0x03 };
diff --git a/themes/Bear2/openbox-3/desk.xbm b/themes/Bear2/openbox-3/desk.xbm
new file mode 100644
index 0000000..f6e24ce
--- /dev/null
+++ b/themes/Bear2/openbox-3/desk.xbm
@@ -0,0 +1,4 @@
+#define desk_width 8
+#define desk_height 8
+static unsigned char desk_bits[] = {
+ 0x00, 0x00, 0x48, 0x78, 0x7f, 0x78, 0x48, 0x00 };
diff --git a/themes/Bear2/openbox-3/desk_toggled.xbm b/themes/Bear2/openbox-3/desk_toggled.xbm
new file mode 100644
index 0000000..d89d4bb
--- /dev/null
+++ b/themes/Bear2/openbox-3/desk_toggled.xbm
@@ -0,0 +1,4 @@
+#define desk_toggled_width 8
+#define desk_toggled_height 8
+static unsigned char desk_toggled_bits[] = {
+ 0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x00 };
diff --git a/themes/Bear2/openbox-3/iconify.xbm b/themes/Bear2/openbox-3/iconify.xbm
new file mode 100644
index 0000000..0f415e0
--- /dev/null
+++ b/themes/Bear2/openbox-3/iconify.xbm
@@ -0,0 +1,4 @@
+#define iconify_width 8
+#define iconify_height 8
+static unsigned char iconify_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff };
diff --git a/themes/Bear2/openbox-3/iconify_pressed.xbm b/themes/Bear2/openbox-3/iconify_pressed.xbm
new file mode 100644
index 0000000..28a9cab
--- /dev/null
+++ b/themes/Bear2/openbox-3/iconify_pressed.xbm
@@ -0,0 +1,5 @@
+#define iconify_pressed_width 10
+#define iconify_pressed_height 10
+static unsigned char iconify_pressed_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0xfc, 0x03 };
diff --git a/themes/Bear2/openbox-3/max.xbm b/themes/Bear2/openbox-3/max.xbm
new file mode 100644
index 0000000..3a13089
--- /dev/null
+++ b/themes/Bear2/openbox-3/max.xbm
@@ -0,0 +1,4 @@
+#define max_width 8
+#define max_height 8
+static unsigned char max_bits[] = {
+ 0xff, 0xff, 0x81, 0x81, 0x81, 0x81, 0x81, 0xff };
diff --git a/themes/Bear2/openbox-3/max_pressed.xbm b/themes/Bear2/openbox-3/max_pressed.xbm
new file mode 100644
index 0000000..5116ae3
--- /dev/null
+++ b/themes/Bear2/openbox-3/max_pressed.xbm
@@ -0,0 +1,5 @@
+#define max_pressed_width 10
+#define max_pressed_height 10
+static unsigned char max_pressed_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0xfc, 0x03, 0x04, 0x02, 0x04, 0x02,
+ 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0xfc, 0x03 };
diff --git a/themes/Bear2/openbox-3/max_toggled.xbm b/themes/Bear2/openbox-3/max_toggled.xbm
new file mode 100644
index 0000000..ff1c954
--- /dev/null
+++ b/themes/Bear2/openbox-3/max_toggled.xbm
@@ -0,0 +1,4 @@
+#define max_toggled_width 8
+#define max_toggled_height 8
+static unsigned char max_toggled_bits[] = {
+ 0xfc, 0xfc, 0x84, 0x9f, 0x91, 0xf1, 0x11, 0x1f };
diff --git a/themes/Bear2/openbox-3/shade.xbm b/themes/Bear2/openbox-3/shade.xbm
new file mode 100644
index 0000000..ff774f8
--- /dev/null
+++ b/themes/Bear2/openbox-3/shade.xbm
@@ -0,0 +1,4 @@
+#define shade_width 8
+#define shade_height 8
+static unsigned char shade_bits[] = {
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
diff --git a/themes/Bear2/openbox-3/shade_pressed.xbm b/themes/Bear2/openbox-3/shade_pressed.xbm
new file mode 100644
index 0000000..d65fe7a
--- /dev/null
+++ b/themes/Bear2/openbox-3/shade_pressed.xbm
@@ -0,0 +1,5 @@
+#define shade_pressed_width 10
+#define shade_pressed_height 10
+static unsigned char shade_pressed_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
diff --git a/themes/Bear2/openbox-3/themerc b/themes/Bear2/openbox-3/themerc
new file mode 100644
index 0000000..b0db53e
--- /dev/null
+++ b/themes/Bear2/openbox-3/themerc
@@ -0,0 +1,83 @@
+!! Bear2 by David Barr <david@chalkskeletons.com>
+!! http://david.chalkskeletons.com
+!! another blue theme
+
+!!General
+
+window.handle.width: 4
+window.client.padding.width: 0
+window.client.padding.height: 0
+border.Width: 1
+padding.width: 3
+menu.overlap: 2
+border.color: #4e4e4e
+window.frameColor: #eeeeec
+window.*.client.color: #eeeeec
+*.text.justify: center
+
+!!Fonts
+
+window.active.label.text.font: shadow=y:shadowoffset=1:shadowtint=3
+window.inactive.label.text.font: shadow=y:shadowoffset=1:shadowtint=0
+menu.items.font:
+menu.title.text.font: shadow=y:shadowoffset=1:shadowtint=30
+
+!!Menu
+menu.border.color: #9d9d9d
+menu.title.bg: flat border vertical gradient
+menu.title.bg.color: #3465A4
+menu.title.bg.colorTo: #407CCA
+menu.title.bg.border.color: #729fcf
+menu.title.text.color: #ffffff
+
+menu.items.bg: flat border solid
+menu.items.bg.color: #eeeeec
+menu.items.bg.border.color: #EFEBE7
+
+menu.items.text.color: #444444
+menu.items.disabled.text.color: #babdb6
+
+menu.items.active.bg: flat solid
+menu.items.active.bg.color: #4481c0
+menu.items.active.bg.colorTo: #4175aa
+menu.items.active.text.color: #f6f8fb
+menu.items.active.bg.border.color: #416c98
+
+!!Active
+
+window.active.title.bg: flat border gradient osx
+window.active.title.bg.color: #3465A4
+window.active.title.bg.colorTo: #407CCA
+window.active.title.bg.border.color: #699acd
+
+window.active.label.bg: parentrelative
+window.active.label.text.color: #ffffff
+
+window.active.button.*.bg: parentrelative
+window.active.button.*.image.color: #efefef
+window.active.button.hover.bg.color: #729fcf
+window.active.button.hover.image.color: #ffffff
+window.active.button.pressed.bg.color: #a7cef2
+window.active.button.disabled.image.color: #6d8fbc
+
+window.active.handle.bg: raised solid
+window.active.handle.bg.color: #E7e7e7
+window.*.grip.bg: parentrelative
+
+!!Inactive
+
+window.inactive.title.bg: flat border gradient osx
+window.inactive.title.bg.color: #dcdcdc
+window.inactive.title.bg.colorTo: #eeeeec
+window.inactive.title.bg.border.color: #efefef
+window.inactive.label.bg: parentrelative
+window.inactive.label.text.color: #888a85
+
+window.inactive.button.*.bg: parentrelative
+window.inactive.button.*.image.color: #888a85
+window.inactive.button.pressed.bg.color: #d3d7cf
+window.inactive.button.pressed.bg.color: #4f5051
+window.inactive.button.disabled.image.color: #d0d0d0
+
+window.inactive.handle.bg: raised solid
+window.inactive.handle.bg.color: #E7e7e7
diff --git a/themes/Clearlooks-3.4/openbox-3/themerc b/themes/Clearlooks-3.4/openbox-3/themerc
new file mode 100644
index 0000000..05e8922
--- /dev/null
+++ b/themes/Clearlooks-3.4/openbox-3/themerc
@@ -0,0 +1,126 @@
+# Name: Clearlooks
+# Author: John McKnight <jmcknight@gmail.com>
+# Note: This is a port of the Clearlooks metacity theme to Openbox.
+
+### Menu
+#menu.border.color: #b5aa99
+
+menu.title.bg: Flat Border Gradient Vertical
+menu.title.bg.color: #589bda
+menu.title.bg.colorTo: #3c7cb7
+menu.title.bg.border.color: #7cb6ec
+menu.title.text.color: #ffffff
+menu.title.text.justify: Center
+
+menu.items.bg: Flat Solid
+menu.items.bg.color: #fcfbfa
+menu.items.text.color: #000000
+menu.items.disable.text.color: #b5b3ac
+
+menu.items.active.bg: Flat Gradient Vertical
+menu.items.active.bg.color: #5c9edb
+menu.items.active.bg.colorTo: #4489ca
+menu.items.active.text.color: #ffffff
+
+
+### Window active
+window.active.title.bg: Flat Border Gradient Vertical
+window.active.title.bg.color: #589bda
+window.active.title.bg.colorTo: #3c7cb7
+window.active.title.bg.border.color: #7cb6ec
+window.active.title.separator.color: #334c62
+
+window.active.label.bg: Parentrelative
+window.active.label.text.color: #ffffff
+
+window.active.handle.bg: Flat Border Solid
+window.active.handle.bg.color: #3c7cb7
+window.active.handle.bg.border.color: #7cb6ec
+
+window.active.grip.bg: Flat Border Solid
+window.active.grip.bg.color: #3c7cb7
+window.active.grip.bg.border.color: #7cb6ec
+
+window.active.button.unpressed.bg: Flat Border Gradient Vertical
+window.active.button.unpressed.bg.color: #5ea0dd
+window.active.button.unpressed.bg.colorTo: #3f85c5
+window.active.button.unpressed.bg.border.color: #36536f
+window.active.button.unpressed.image.color: #ffffff
+
+window.active.button.pressed.bg: Flat Border Gradient Vertical
+window.active.button.pressed.bg.color: #3c82c3
+window.active.button.pressed.bg.colorTo: #3c7ab5
+window.active.button.pressed.bg.border.color: #36536f
+window.active.button.pressed.image.color: #ffffff
+
+window.active.button.disabled.bg: Flat Border Gradient Vertical
+window.active.button.disabled.bg.color: #3c82c3
+window.active.button.disabled.bg.colorTo: #3c7ab5
+window.active.button.disabled.bg.border.color: #36536f
+window.active.button.disabled.image.color: #36536f
+
+window.active.button.toggled.bg: Flat Border Gradient Vertical
+window.active.button.toggled.bg.color: #5ea0dd
+window.active.button.toggled.bg.colorTo: #3f85c5
+window.active.button.toggled.bg.border.color: #36536f
+window.active.button.toggled.image.color: #dcd4c9
+
+
+### Window inactive
+window.inactive.border.color: #3d3a37
+
+window.inactive.title.bg: Flat Border Gradient Vertical
+window.inactive.title.bg.color: #efece6
+window.inactive.title.bg.colorTo: #d9d2c7
+window.inactive.title.bg.border.color: #ffffff
+window.inactive.title.separator.color: #9a8e7c
+
+window.inactive.label.bg: Parentrelative
+window.inactive.label.text.color: #000000
+
+window.inactive.handle.bg: Flat Border Solid
+window.inactive.handle.bg.color: #d9d2c7
+window.inactive.handle.bg.border.color: #ffffff
+
+window.inactive.grip.bg: Flat Border Solid
+window.inactive.grip.bg.color: #d9d2c7
+window.inactive.grip.bg.border.color: #ffffff
+
+window.inactive.button.unpressed.bg: Flat Border Gradient Vertical
+window.inactive.button.unpressed.bg.color: #ede9e3
+window.inactive.button.unpressed.bg.colorTo: #dbd5ca
+window.inactive.button.unpressed.bg.border.color: #8f8370
+window.inactive.button.unpressed.image.color: #000000
+
+window.inactive.button.pressed.bg: Flat Border Gradient Vertical
+window.inactive.button.pressed.bg.color: #ede9e3
+window.inactive.button.pressed.bg.colorTo: #dbd5ca
+window.inactive.button.pressed.bg.border.color: #8f8370
+window.inactive.button.pressed.image.color: #000000
+
+window.inactive.button.disabled.bg: Flat Border Gradient Vertical
+window.inactive.button.disabled.bg.color: #ede9e3
+window.inactive.button.disabled.bg.colorTo: #dbd5ca
+window.inactive.button.disabled.bg.border.color: #8f8370
+window.inactive.button.disabled.image.color: #000000
+
+window.inactive.button.toggled.bg: Flat Border Gradient Vertical
+window.inactive.button.toggled.bg.color: #ede9e3
+window.inactive.button.toggled.bg.colorTo: #dbd5ca
+window.inactive.button.toggled.bg.border.color: #8f8370
+window.inactive.button.toggled.image.color: #000000
+
+
+### Everything else
+border.width: 1
+padding.width: 2
+window.handle.width: 4
+window.client.padding.width: 0
+border.color: #1f252b
+menu.overlap: 0
+
+### Fonts
+window.active.label.text.font:
+window.inactive.label.text.font:
+menu.items.font:
+menu.title.text.font:
diff --git a/themes/Clearlooks-Olive/openbox-3/themerc b/themes/Clearlooks-Olive/openbox-3/themerc
new file mode 100644
index 0000000..249f0e6
--- /dev/null
+++ b/themes/Clearlooks-Olive/openbox-3/themerc
@@ -0,0 +1,121 @@
+# Name: Clearlooks-Olive
+# Author: John McKnight <jmcknight@gmail.com>
+# Note: This is a port of the Clearlooks-Olive metacity theme to Openbox.
+
+# Menu settings
+menu.title.bg: Raised Gradient Vertical
+menu.title.bg.color: #95ad70
+menu.title.bg.colorTo: #7d925d
+menu.title.text.color: #ffffff
+menu.title.text.justify: Left
+
+menu.items.bg: Flat Solid
+menu.items.bg.color: #f1eee0
+menu.items.text.color: #444444
+menu.items.disabled.text.color: #aaaaaa
+menu.items.active.disabled.text.color: #969696
+
+menu.items.active.bg: Flat Gradient Vertical Border
+menu.items.active.bg.color: #b4c994
+menu.items.active.bg.colorTo: #92aa6d
+menu.items.active.bg.border.color: #92aa6d
+menu.items.active.text.color: #ffffff
+
+
+# Window settings (focused)
+window.active.title.bg: Raised Gradient Vertical
+window.active.title.bg.color: #95ad70
+window.active.title.bg.colorTo: #7d925d
+
+window.active.label.bg: Parentrelative
+window.active.label.text.color: #ffffff
+
+window.active.handle.bg: Raised Gradient Vertical
+window.active.handle.bg.color: #95ad70
+window.active.handle.bg.colorTo: #7d925d
+
+window.active.grip.bg: Raised Gradient Vertical
+window.active.grip.bg.color: #95ad70
+window.active.grip.bg.colorTo: #7d925d
+
+window.active.button.unpressed.bg: Flat Gradient Vertical Border
+window.active.button.unpressed.bg.color: #65744e
+window.active.button.unpressed.bg.colorTo: #5b6846
+window.active.button.unpressed.bg.border.color: #abbc91
+window.active.button.unpressed.image.color: #ffffff
+
+window.active.button.pressed.bg: Flat Gradient Vertical Border
+window.active.button.pressed.bg.color: #373d2b
+window.active.button.pressed.bg.colorTo: #333828
+window.active.button.pressed.bg.border.color: #abbc91
+window.active.button.pressed.image.color: #ffffff
+
+window.active.button.disabled.bg: Flat Gradient Vertical Border
+window.active.button.disabled.bg.color: #373d2b
+window.active.button.disabled.bg.colorTo: #333828
+window.active.button.disabled.bg.border.color: #abbc91
+window.active.button.disabled.image.color: #abbc91
+
+window.active.button.toggled.bg: Flat Gradient Vertical Border
+window.active.button.toggled.bg.color: #65744e
+window.active.button.toggled.bg.colorTo: #5b6846
+window.active.button.toggled.bg.border.color: #abbc91
+window.active.button.toggled.image.color: #b9b7a7
+
+
+# Window settings (unfocused)
+window.inactive.title.bg: Raised Gradient Vertical
+window.inactive.title.bg.color: #f1eeea
+window.inactive.title.bg.colorTo: #d8cfc7
+
+window.inactive.label.bg: Parentrelative
+window.inactive.label.text.color: #000000
+
+window.inactive.handle.bg: Raised Gradient Vertical
+window.inactive.handle.bg.color: #f1eeea
+window.inactive.handle.bg.colorTo: #d8cfc7
+
+window.inactive.grip.bg: Raised Gradient Vertical
+window.inactive.grip.bg.color: #f1eeea
+window.inactive.grip.bg.colorTo: #d8cfc7
+
+window.inactive.button.unpressed.bg: Flat Gradient Vertical Border
+window.inactive.button.unpressed.bg.color: #efebe7
+window.inactive.button.unpressed.bg.colorTo: #ddd6ce
+window.inactive.button.unpressed.bg.border.color: #8f8173
+window.inactive.button.unpressed.image.color: #000000
+
+window.inactive.button.pressed.bg: Flat Gradient Vertical Border
+window.inactive.button.pressed.bg.color: #efebe7
+window.inactive.button.pressed.bg.colorTo: #ddd6ce
+window.inactive.button.pressed.bg.border.color: #8f8173
+window.inactive.button.pressed.image.color: #000000
+
+window.inactive.button.disabled.bg: Flat Gradient Vertical Border
+window.inactive.button.disabled.bg.color: #efebe7
+window.inactive.button.disabled.bg.colorTo: #ddd6ce
+window.inactive.button.disabled.bg.border.color: #8f8173
+window.inactive.button.disabled.image.color: #8f8173
+
+window.inactive.button.toggled.bg: Flat Gradient Vertical Border
+window.inactive.button.toggled.bg.color: #efebe7
+window.inactive.button.toggled.bg.colorTo: #ddd6ce
+window.inactive.button.toggled.bg.border.color: #8f8173
+window.inactive.button.toggled.image.color: #000000
+
+
+### Everything else
+border.width: 1
+padding.width: 2
+window.handle.width: 3
+window.client.padding.width: 0
+window.client.padding.height: 0
+border.color: #000000
+menu.overlap: 0
+
+
+### Fonts
+window.active.label.text.font:shadow=y:shadowtint=70:shadowoffset=1
+window.inactive.label.text.font:shadow=y:shadowtint=20:shadowoffset=1
+menu.items.font:
+menu.title.text.font:shadow=y:shadowtint=70
diff --git a/themes/Clearlooks/openbox-3/themerc b/themes/Clearlooks/openbox-3/themerc
new file mode 100644
index 0000000..f81c428
--- /dev/null
+++ b/themes/Clearlooks/openbox-3/themerc
@@ -0,0 +1,164 @@
+!# Clearlooks-Evolving
+!# Clearlooks as it evolves in gnome-git...
+!# Last updated 09/03/10
+
+# Fonts
+# these are really halos, but who cares?
+
+*.font: shadow=n
+window.active.label.text.font:shadow=y:shadowtint=30:shadowoffset=1
+window.inactive.label.text.font:shadow=y:shadowtint=00:shadowoffset=0
+menu.items.font:shadow=y:shadowtint=0:shadowoffset=1
+
+!# general stuff
+
+border.width: 1
+padding.width: 3
+padding.height: 2
+window.handle.width: 3
+window.client.padding.width: 0
+menu.overlap: 2
+*.justify: center
+
+!# lets set our damn shadows here, eh?
+
+*.bg.highlight: 50
+*.bg.shadow: 05
+
+window.active.title.bg.highlight: 35
+window.active.title.bg.shadow: 05
+
+window.inactive.title.bg.highlight: 30
+window.inactive.title.bg.shadow: 05
+
+window.*.grip.bg.highlight: 50
+window.*.grip.bg.shadow: 30
+
+window.*.handle.bg.highlight: 50
+window.*.handle.bg.shadow: 30
+
+!# Menu settings
+
+menu.border.color: #aaaaaa
+menu.border.width: 1
+
+menu.title.bg: solid flat
+menu.title.bg.color: #E6E7E6
+menu.title.text.color: #111111
+
+menu.items.bg: Flat Solid
+menu.items.bg.color: #ffffff
+menu.items.text.color: #111111
+menu.items.disabled.text.color: #aaaaaa
+
+menu.items.active.bg: Flat Gradient splitvertical border
+
+menu.items.active.bg.color: #97b8e2
+menu.items.active.bg.color.splitTo: #a8c5e9
+
+menu.items.active.bg.colorTo: #91b3de
+menu.items.active.bg.colorTo.splitTo: #80a7d6
+menu.items.active.bg.border.color: #4b6e99
+menu.items.active.text.color: #ffffff
+
+menu.separator.width: 1
+menu.separator.padding.width: 0
+menu.separator.padding.height: 3
+menu.separator.color: #aaaaaa
+
+!# set handles here and only the once?
+
+window.*.handle.bg: Raised solid
+window.*.handle.bg.color: #eaebec
+
+window.*.grip.bg: Raised solid
+window.*.grip.bg.color: #eaebec
+
+!# Active
+
+window.*.border.color: #585a5d
+
+window.active.title.separator.color: #4e76a8
+
+*.title.bg: Raised Gradient splitvertical
+*.title.bg.color: #8CB0DC
+*.title.bg.color.splitTo: #99BAE3
+*.title.bg.colorTo: #86ABD9
+*.title.bg.colorTo.splitTo: #7AA1D1
+
+window.active.label.bg: Parentrelative
+window.active.label.text.color: #ffffff
+
+window.active.button.*.bg: Flat Gradient splitvertical Border
+
+window.active.button.*.bg.color: #92B4DF
+window.active.button.*.bg.color.splitTo: #B0CAEB
+window.active.button.*.bg.colorTo: #86ABD9
+window.active.button.*.bg.colorTo.splitTo: #769FD0
+
+window.active.button.*.bg.border.color: #49678B
+window.active.button.*.image.color: #F4F5F6
+
+window.active.button.hover.bg.color: #b5d3ef
+window.active.button.hover.bg.color.splitTo: #b5d3ef
+window.active.button.hover.bg.colorTo: #9cbae7
+window.active.button.hover.bg.colorTo.splitTo: #8caede
+window.active.button.hover.bg.border.color: #4A658C
+window.active.button.hover.image.color: #ffffff
+
+window.active.button.pressed.bg: Flat solid Border
+window.active.button.pressed.bg.color: #7aa1d2
+
+window.active.button.hover.bg.border.color: #4A658C
+
+!# inactive
+
+!#window.inactive.border.color: #7e8285
+window.inactive.title.separator.color: #96999d
+
+window.inactive.title.bg: Raised Gradient splitvertical
+window.inactive.title.bg.color: #E3E2E0
+window.inactive.title.bg.color.splitTo: #EBEAE9
+window.inactive.title.bg.colorTo: #DEDCDA
+window.inactive.title.bg.colorTo.splitTo: #D5D3D1
+
+window.inactive.label.bg: Parentrelative
+window.inactive.label.text.color: #70747d
+
+window.inactive.button.*.bg: Flat Gradient splitVertical Border
+window.inactive.button.*.bg.color: #ffffff
+window.inactive.button.*.bg.color.splitto: #ffffff
+window.inactive.button.*.bg.colorTo: #F9F8F8
+window.inactive.button.*.bg.colorTo.splitto: #E9E7E6
+window.inactive.button.*.bg.border.color: #928F8B
+window.inactive.button.*.image.color: #6D6C6C
+
+
+!# osd (pop ups and what not, dock?)
+
+osd.border.width: 1
+osd.border.color: #aaaaaa
+
+osd.bg: flat border gradient splitvertical
+osd.bg.color: #F0EFEE
+osd.bg.color.splitto: #f5f5f4
+osd.bg.colorTo: #EAEBEC
+osd.bg.colorTo.splitto: #E7E5E4
+
+osd.bg.border.color: #ffffff
+
+osd.active.label.bg: parentrelative
+osd.active.label.bg.color: #efefef
+osd.active.label.bg.border.color: #9c9e9c
+osd.active.label.text.color: #444
+
+osd.inactive.label.bg: parentrelative
+osd.inactive.label.text.color: #70747d
+
+!# yeah whatever, this is fine anyhoo?
+osd.hilight.bg: flat vertical gradient
+osd.hilight.bg.color: #9ebde5
+osd.hilight.bg.colorTo: #749dcf
+osd.unhilight.bg: flat vertical gradient
+osd.unhilight.bg.color: #BABDB6
+osd.unhilight.bg.colorTo: #efefef
diff --git a/themes/Makefile b/themes/Makefile
new file mode 100644
index 0000000..b90edac
--- /dev/null
+++ b/themes/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/themes/Mikachu/openbox-3/bullet.xbm b/themes/Mikachu/openbox-3/bullet.xbm
new file mode 100644
index 0000000..88481ec
--- /dev/null
+++ b/themes/Mikachu/openbox-3/bullet.xbm
@@ -0,0 +1,4 @@
+#define bullet_width 4
+#define bullet_height 7
+static unsigned char bullet_bits[] = {
+ 0x01, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x01 };
diff --git a/themes/Mikachu/openbox-3/close.xbm b/themes/Mikachu/openbox-3/close.xbm
new file mode 100644
index 0000000..aea5488
--- /dev/null
+++ b/themes/Mikachu/openbox-3/close.xbm
@@ -0,0 +1,5 @@
+/* Created with The GIMP */
+#define close_width 8
+#define close_height 8
+static unsigned char close_bits[] = {
+ 0x00, 0xc3, 0x66, 0x3c, 0x3c, 0x66, 0xc3, 0x00 };
diff --git a/themes/Mikachu/openbox-3/desk.xbm b/themes/Mikachu/openbox-3/desk.xbm
new file mode 100644
index 0000000..4345dfa
--- /dev/null
+++ b/themes/Mikachu/openbox-3/desk.xbm
@@ -0,0 +1,5 @@
+/* Created with The GIMP */
+#define stick_width 8
+#define stick_height 8
+static unsigned char stick_bits[] = {
+ 0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x00 };
diff --git a/themes/Mikachu/openbox-3/iconify.xbm b/themes/Mikachu/openbox-3/iconify.xbm
new file mode 100644
index 0000000..1cd40fc
--- /dev/null
+++ b/themes/Mikachu/openbox-3/iconify.xbm
@@ -0,0 +1,5 @@
+/* Created with The GIMP */
+#define icon_width 8
+#define icon_height 8
+static unsigned char icon_bits[] = {
+ 0x00, 0x00, 0x42, 0x66, 0x3c, 0x18, 0x00, 0x00 };
diff --git a/themes/Mikachu/openbox-3/max.xbm b/themes/Mikachu/openbox-3/max.xbm
new file mode 100644
index 0000000..2cd94e8
--- /dev/null
+++ b/themes/Mikachu/openbox-3/max.xbm
@@ -0,0 +1,5 @@
+/* Created with The GIMP */
+#define max_width 8
+#define max_height 8
+static unsigned char max_bits[] = {
+ 0x00, 0x00, 0x18, 0x3c, 0x66, 0x42, 0x00, 0x00 };
diff --git a/themes/Mikachu/openbox-3/themerc b/themes/Mikachu/openbox-3/themerc
new file mode 100644
index 0000000..ae7e56b
--- /dev/null
+++ b/themes/Mikachu/openbox-3/themerc
@@ -0,0 +1,174 @@
+!! Menu settings
+
+menu.title.bg: raised gradient crossdiagonal bevel1
+menu.title.bg.color: #6699CC
+menu.title.bg.colorTo: #334866
+menu.title.bg.border.color: #000000
+menu.title.text.color: #CCCCFF
+menu.title.text.justify: center
+
+menu.items.bg: flat gradient vertical bevel1
+menu.items.bg.color: #B8B8D8
+menu.items.bg.colorTo: #A0A0BA
+menu.items.bg.border.color: #000000
+menu.items.text.color: #000022
+menu.items.disabled.text.color: #711
+
+menu.items.active.bg: raised gradient mirrorhorizontal
+menu.items.active.bg.color: #555577
+menu.items.active.bg.colorTo: #6699CC
+menu.items.active.bg.border.color: #000000
+menu.items.active.text.color: #CCCCFF
+
+menu.frame.justify: left
+
+!! General window settings
+window.label.text.justify: left
+
+!! focused window settings
+window.active.client.color: #8080A0
+
+window.active.title.bg: raised gradient crossdiagonal bevel1
+window.active.title.bg.color: #6699CC
+window.active.title.bg.colorTo: #334866
+window.active.title.bg.border.color: #000000
+
+window.active.handle.bg: flat gradient crossdiagonal bevel1
+window.active.handle.bg.color: #6699CC
+window.active.handle.bg.colorTo: #334866
+window.active.handle.bg.border.color: #000000
+
+window.active.grip.bg: parentrelative
+window.active.grip.bg.color: #000000
+window.active.grip.bg.colorTo: #000000
+window.active.grip.bg.border.color: #000000
+
+window.active.label.bg: parentrelative
+window.active.label.bg.color: #000000
+window.active.label.bg.colorTo: #000000
+window.active.label.bg.border.color: #000000
+window.active.label.text.color: #BFE9FF
+
+window.active.button.unpressed.bg: parentrelative
+window.active.button.unpressed.bg.color: #000000
+window.active.button.unpressed.bg.colorTo: #000000
+window.active.button.unpressed.bg.border.color: #000000
+window.active.button.unpressed.image.color: grey85
+
+window.active.button.pressed.bg: sunken parentrelative bevel1
+window.active.button.pressed.bg.color: #6699CC
+window.active.button.pressed.bg.colorTo: #334866
+window.active.button.pressed.bg.border.color: #000000
+window.active.button.pressed.image.color: green
+
+window.active.button.toggled.bg: sunken parentrelative bevel2
+window.active.button.toggled.bg.color: #6699CC
+window.active.button.toggled.bg.colorTo: #334866
+window.active.button.toggled.bg.border.color: #000000
+window.active.button.toggled.image.color: grey85
+
+window.active.button.toggled.hover.bg: sunken parentrelative bevel2
+window.active.button.toggled.hover.bg.color: #6699CC
+window.active.button.toggled.hover.bg.colorTo: #334866
+window.active.button.toggled.hover.bg.border.color: #000000
+window.active.button.toggled.hover.image.color: #00FF00
+
+window.active.button.disabled.bg: parentrelative
+window.active.button.disabled.bg.color: #000000
+window.active.button.disabled.bg.colorTo: #000000
+window.active.button.disabled.image.color: #000000
+window.active.button.disabled.bg.border.color: #000000
+
+window.active.button.hover.bg: parentrelative
+window.active.button.hover.image.color: #00FF00
+window.active.button.hover.bg.color: #000000
+window.active.button.hover.bg.colorTo: #000000
+window.active.button.hover.bg.border.color: #000000
+
+!window.active.button.hover.bg: raised gradient crossdiagonal bevel1
+!window.active.button.hover.image.color: #00FF00
+!window.active.button.hover.bg.color: #334866
+!window.active.button.hover.bg.colorTo: #6699CC
+!window.active.button.hover.bg.border.color: #000000
+
+!! unfocused window settings
+window.inactive.client.color: grey50
+
+window.inactive.title.bg: flat gradient diagonal bevel1
+window.inactive.title.bg.color: #7F7FA0
+window.inactive.title.bg.colorTo: #333350
+window.inactive.title.bg.border.color: #000000
+
+window.inactive.handle.bg: flat gradient diagonal bevel1
+window.inactive.handle.bg.color: grey50
+window.inactive.handle.bg.colorTo: grey20
+window.inactive.handle.bg.border.color: #000000
+
+window.inactive.grip.bg: parentrelative
+window.inactive.grip.bg.color: #000000
+window.inactive.grip.bg.colorTo: #000000
+window.inactive.grip.bg.border.color: #000000
+
+window.inactive.label.bg: parentrelative
+window.inactive.label.bg.color: #000000
+window.inactive.label.bg.colorTo: #000000
+window.inactive.label.bg.border.color: #000000
+window.inactive.label.text.color: #C3C3E0
+
+window.inactive.button.unpressed.bg: parentrelative
+window.inactive.button.unpressed.bg.color: #000000
+window.inactive.button.unpressed.bg.colorTo: #000000
+window.inactive.button.unpressed.bg.border.color: #000000
+window.inactive.button.unpressed.image.color: grey60
+
+window.inactive.button.pressed.bg: sunken parentrelative bevel1
+window.inactive.button.pressed.bg.color: grey60
+window.inactive.button.pressed.bg.colorTo: grey20
+window.inactive.button.pressed.bg.border.color: #000000
+window.inactive.button.pressed.image.color: #00CC00
+
+window.inactive.button.disabled.bg: parentrelative
+window.inactive.button.disabled.bg.color: #000000
+window.inactive.button.disabled.bg.colorTo: #000000
+window.inactive.button.disabled.bg.border.color: #000000
+window.inactive.button.disabled.image.color: #000000
+
+window.inactive.button.toggled.bg: sunken parentrelative bevel2
+window.inactive.button.toggled.bg.color: grey60
+window.inactive.button.toggled.bg.colorTo: grey20
+window.inactive.button.toggled.bg.border.color: #000000
+window.inactive.button.toggled.image.color: grey60
+
+window.inactive.button.toggled.hover.bg: sunken parentrelative bevel2
+window.inactive.button.toggled.hover.bg.color: grey60
+window.inactive.button.toggled.hover.bg.colorTo: grey20
+window.inactive.button.toggled.hover.bg.border.color: #000000
+window.inactive.button.toggled.hover.image.color: #00CC00
+
+window.inactive.button.hover.bg: parentrelative
+window.inactive.button.hover.bg.color: #000000
+window.inactive.button.hover.bg.colorTo: #000000
+window.inactive.button.hover.image.color: #00CC00
+window.inactive.button.hover.bg.border.color: #000000
+
+osd.label.bg: parentrelative sunken
+
+!! Global width settings
+border.width: 1
+padding.width: 1
+window.handle.width: 0
+window.client.padding.width: 0
+window.client.padding.height: 0
+focus.inner.color: #A6CAF3
+focus.outer.color: #0000A0
+
+!! Miscellaneous settings
+border.color: #223344
+
+!! Font stuff (not used anymore)
+window.active.label.text.font: Candara,sans:size=10:shadow=y:shadowoffset=2:shadowtint=32:weight=0
+window.inactive.label.text.font: Candara,sans:size=10:shadowoffset=3:shadowtint=32:shadow=y:weight=0
+menu.title.text.font: Technical,sans:size=20:shadowoffset=2:shadowtint=35:shadow=y
+menu.items.font: Technical,sans:size=10:shadowoffset=2:shadowtint=15:shadow=y
+
+menu.overlap: -3
diff --git a/themes/Natura/openbox-3/close.xbm b/themes/Natura/openbox-3/close.xbm
new file mode 100644
index 0000000..ab7ff80
--- /dev/null
+++ b/themes/Natura/openbox-3/close.xbm
@@ -0,0 +1,4 @@
+#define close_width 8
+#define close_height 8
+static unsigned char close_bits[] = {
+ 0xc3, 0xe7, 0x7e, 0x3c, 0x3c, 0x7e, 0xe7, 0xc3 };
diff --git a/themes/Natura/openbox-3/close_hover.xbm b/themes/Natura/openbox-3/close_hover.xbm
new file mode 100644
index 0000000..94ce59f
--- /dev/null
+++ b/themes/Natura/openbox-3/close_hover.xbm
@@ -0,0 +1,5 @@
+#define close_hover_width 10
+#define close_hover_height 10
+static unsigned char close_hover_bits[] = {
+ 0x03, 0x03, 0x87, 0x03, 0xce, 0x01, 0xfc, 0x00, 0x78, 0x00, 0x78, 0x00,
+ 0xfc, 0x00, 0xce, 0x01, 0x87, 0x03, 0x03, 0x03 };
diff --git a/themes/Natura/openbox-3/desk.xbm b/themes/Natura/openbox-3/desk.xbm
new file mode 100644
index 0000000..6cdbbf1
--- /dev/null
+++ b/themes/Natura/openbox-3/desk.xbm
@@ -0,0 +1,4 @@
+#define desk_width 8
+#define desk_height 8
+static unsigned char desk_bits[] = {
+ 0x00, 0x00, 0x48, 0x78, 0x7e, 0x78, 0x48, 0x00 };
diff --git a/themes/Natura/openbox-3/desk_hover.xbm b/themes/Natura/openbox-3/desk_hover.xbm
new file mode 100644
index 0000000..5e385d6
--- /dev/null
+++ b/themes/Natura/openbox-3/desk_hover.xbm
@@ -0,0 +1,4 @@
+#define desk_hover_width 8
+#define desk_hover_height 8
+static unsigned char desk_hover_bits[] = {
+ 0x00, 0x88, 0x88, 0xf8, 0xff, 0xf8, 0x88, 0x88 };
diff --git a/themes/Natura/openbox-3/desk_toggled.xbm b/themes/Natura/openbox-3/desk_toggled.xbm
new file mode 100644
index 0000000..d89d4bb
--- /dev/null
+++ b/themes/Natura/openbox-3/desk_toggled.xbm
@@ -0,0 +1,4 @@
+#define desk_toggled_width 8
+#define desk_toggled_height 8
+static unsigned char desk_toggled_bits[] = {
+ 0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x00 };
diff --git a/themes/Natura/openbox-3/iconify.xbm b/themes/Natura/openbox-3/iconify.xbm
new file mode 100644
index 0000000..424cc67
--- /dev/null
+++ b/themes/Natura/openbox-3/iconify.xbm
@@ -0,0 +1,4 @@
+#define iconify_width 8
+#define iconify_height 8
+static unsigned char iconify_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e };
diff --git a/themes/Natura/openbox-3/iconify_hover.xbm b/themes/Natura/openbox-3/iconify_hover.xbm
new file mode 100644
index 0000000..0f56602
--- /dev/null
+++ b/themes/Natura/openbox-3/iconify_hover.xbm
@@ -0,0 +1,4 @@
+#define iconify_hover_width 8
+#define iconify_hover_height 8
+static unsigned char iconify_hover_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff };
diff --git a/themes/Natura/openbox-3/max.xbm b/themes/Natura/openbox-3/max.xbm
new file mode 100644
index 0000000..3a13089
--- /dev/null
+++ b/themes/Natura/openbox-3/max.xbm
@@ -0,0 +1,4 @@
+#define max_width 8
+#define max_height 8
+static unsigned char max_bits[] = {
+ 0xff, 0xff, 0x81, 0x81, 0x81, 0x81, 0x81, 0xff };
diff --git a/themes/Natura/openbox-3/max_hover.xbm b/themes/Natura/openbox-3/max_hover.xbm
new file mode 100644
index 0000000..247c492
--- /dev/null
+++ b/themes/Natura/openbox-3/max_hover.xbm
@@ -0,0 +1,5 @@
+#define max_hover_width 10
+#define max_hover_height 10
+static unsigned char max_hover_bits[] = {
+ 0xff, 0x03, 0xff, 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+ 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xff, 0x03 };
diff --git a/themes/Natura/openbox-3/max_toggled.xbm b/themes/Natura/openbox-3/max_toggled.xbm
new file mode 100644
index 0000000..ff1c954
--- /dev/null
+++ b/themes/Natura/openbox-3/max_toggled.xbm
@@ -0,0 +1,4 @@
+#define max_toggled_width 8
+#define max_toggled_height 8
+static unsigned char max_toggled_bits[] = {
+ 0xfc, 0xfc, 0x84, 0x9f, 0x91, 0xf1, 0x11, 0x1f };
diff --git a/themes/Natura/openbox-3/shade.xbm b/themes/Natura/openbox-3/shade.xbm
new file mode 100644
index 0000000..aaf1ec7
--- /dev/null
+++ b/themes/Natura/openbox-3/shade.xbm
@@ -0,0 +1,4 @@
+#define shade_width 8
+#define shade_height 8
+static unsigned char shade_bits[] = {
+ 0x7e, 0x7e, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00 };
diff --git a/themes/Natura/openbox-3/shade_hover.xbm b/themes/Natura/openbox-3/shade_hover.xbm
new file mode 100644
index 0000000..756842a
--- /dev/null
+++ b/themes/Natura/openbox-3/shade_hover.xbm
@@ -0,0 +1,4 @@
+#define shade_hover_width 8
+#define shade_hover_height 8
+static unsigned char shade_hover_bits[] = {
+ 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 };
diff --git a/themes/Natura/openbox-3/themerc b/themes/Natura/openbox-3/themerc
new file mode 100644
index 0000000..fff1631
--- /dev/null
+++ b/themes/Natura/openbox-3/themerc
@@ -0,0 +1,99 @@
+!! Natura by quandar (http://deletefactory.net/quandar)
+
+!! Global Width
+window.handle.width: 2
+
+window.client.padding.width: 0
+window.client.padding.height: 0
+border.Width: 1
+padding.width: 2
+
+window.active.border.color: #000000
+window.inactive.border.color: #000000
+menu.border.color: #000000
+menu.overlap: 0
+window.frameColor: #eeeeee
+window.*.client.color: #eeeeee
+*.text.justify: left
+
+!! Menu
+menu.title.bg: flat gradient split
+menu.title.bg.color: #5c4e45
+menu.title.bg.colorTo: #51443e
+menu.title.text.color: #ffffff
+
+menu.items.bg: flat solid
+menu.items.bg.color: #f9f2ee
+
+menu.items.text.color: #000000
+menu.items.disabled.text.color: #737573
+
+menu.items.active.bg: flat gradient split
+menu.items.active.bg.color: #5c4e45
+menu.items.active.bg.colorTo:#51443e
+menu.items.active.text.color: #f9f2ee
+
+!! Active Windows
+window.active.title.bg: flat gradient split
+window.active.title.bg.color: #5c4e45
+window.active.title.bg.colorTo: #51443e
+window.active.*.bg.border.color: #000000
+
+window.active.label.bg: parentrelative
+window.active.label.text.color: #f9f2ee
+
+window.active.button.*.bg: parentrelative
+window.active.button.*.image.color: #f9f2ee
+window.active.button.*.bg.border.color: #4e5860
+
+window.active.button.hover.bg.color: #576773
+window.active.button.hover.bg.border.color: #9aabb9
+window.active.button.pressed.bg.color: #343b40
+window.active.button.pressed.bg.colorTo: #000000
+window.active.button.pressed.image.color: #b6b6b6
+window.active.button.pressed.bg.border.color: #b1a19e
+
+window.active.button.disabled.bg: parentrelative
+window.active.button.disabled.image.color: #725e51
+
+window.active.handle.bg: flat solid
+window.active.handle.bg.color:#51443e
+
+window.*.grip.bg: flat solid
+window.*.grip.bg.color: #51443e
+
+window.handle.width: 2
+
+!! Inactive Windows
+window.inactive.title.bg: flat gradient vertical
+window.inactive.title.bg.color: #f0ece3
+window.inactive.title.bg.colorTo: #f8f7f2
+window.inactive.*.border.color: #bfbfbf
+
+window.inactive.label.bg: parentrelative
+window.inactive.label.text.color: #919191
+
+window.inactive.button.*.bg: parentrelative
+window.inactive.button.*.bg.color: #eeeeee
+window.inactive.button.*.image.color: #b6b6b6
+window.inactive.button.*.bg.border.color: #c9c9c9
+
+window.inactive.button.pressed.bg.color: #c5c2c5
+window.inactive.button.pressed.bg.border.color: #7b7d7b
+window.inactive.button.pressed.image.color: #999999
+
+window.inactive.button.hover.bg.color: #afb1b2
+window.inactive.button.hover.bg.border.color: #d9dfe4
+window.inactive.button.hover.image.color: #a0a0a0
+window.inactive.button.disabled.bg: parentrelative
+window.inactive.button.disabled.image.color: #dddddd
+
+window.inactive.handle.bg: flat solid
+window.inactive.handle.bg.color: #f9f7f3
+window.inactive.grip.bg: parentrelative
+
+!! Fonts
+window.active.label.text.font: shadow=y:shadowoffset=1:shadowtint=75
+window.inactive.label.text.font: shadow=y:shadowoffset=1:shadowtint=0
+menu.items.font:
+menu.title.text.font: shadow=y:shadowoffset=1:shadowtint=75
diff --git a/themes/Onyx-Citrus/openbox-3/themerc b/themes/Onyx-Citrus/openbox-3/themerc
new file mode 100644
index 0000000..a849342
--- /dev/null
+++ b/themes/Onyx-Citrus/openbox-3/themerc
@@ -0,0 +1,85 @@
+!! Onyx-Citrus by Dana Jansens
+!! inspired by "Carbon" by p0ng, and the ever so popular Vista glass look
+
+!! Menu background
+menu.items.bg: flat gradient vertical
+menu.items.bg.color: #303030
+menu.items.bg.colorTo: #080808
+
+!! Menu text
+menu.items.text.color: #b8b8b8
+menu.items.justify: left
+menu.items.disabled.text.color: #606060
+
+!! Menu headers
+menu.title.bg: raised splitvertical gradient
+menu.title.bg.color: #303030
+menu.title.bg.colorTo: #181818
+menu.title.text.color: white
+menu.title.text.justify: center
+
+!! Selected menu item
+menu.items.active.bg: raised splitvertical gradient
+menu.items.active.bg.color: #e18a51
+menu.items.active.bg.colorTo: #e1621d
+menu.items.active.text.color: white
+
+!! Titlebar
+window.active.title.bg: raised splitvertical gradient
+window.active.title.bg.color: #414141
+window.active.title.bg.colorTo: #202020
+window.inactive.title.bg: gradient splitvertical gradient flat
+window.inactive.title.bg.color: #DEE0D8
+window.inactive.title.bg.colorTo: #E0E0D8
+
+!! Titlebar text
+window.label.text.justify: center
+window.active.label.bg: parentrelative
+window.active.label.text.color: #f8f8f8
+window.inactive.label.bg: parentrelative
+window.inactive.label.text.color: #747474
+
+!! Window buttons
+window.*.button.*.bg: parentrelative
+window.active.button.*.hover.bg: flat splitvertical gradient border
+window.inactive.button.*.hover.bg: parentrelative
+window.*.button.*.pressed.bg: flat splitvertical gradient border
+
+window.active.button.*.hover.bg.color: #e18a51
+window.active.button.*.hover.bg.colorTo: #e1621d
+window.active.button.*.hover.bg.border.color: #ab5d20
+
+window.active.button.*.pressed.bg.color: #99663c
+window.active.button.*.pressed.bg.colorTo: #99663c
+window.active.button.*.pressed.image.color: #888888
+window.active.button.*.pressed.bg.border.color: #683913
+window.inactive.button.*.pressed.bg.color: #ffaa64
+window.inactive.button.*.pressed.bg.colorTo: #ffaa64
+window.inactive.button.*.pressed.image.color: #fdceb7
+window.inactive.button.*.pressed.bg.border.color: #f0832c
+
+window.active.button.*.image.color: #e0e0e0
+window.active.button.disabled.image.color: #707070
+window.inactive.button.*.image.color: #747474
+window.inactive.button.disabled.image.color: #c0c0c0
+
+!! Borders
+window.active.border.color: #181818
+window.inactive.border.color: #404040
+window.inactive.title.separator.color: #eeeee6
+border.width: 1
+padding.width: 2
+window.handle.width: 0
+window.active.client.color: #181818
+window.inactive.client.color: #CACAB6
+window.client.padding.width: 1
+window.client.padding.height: 1
+menu.overlap: 0
+
+!! Font shadows
+menu.items.font:shadow=y:shadowtint=30
+
+!! On-screen displays
+osd.bg: gradient vertical flat
+osd.bg.color: #303030
+osd.bg.colorTo: #080808
diff --git a/themes/Onyx/openbox-3/themerc b/themes/Onyx/openbox-3/themerc
new file mode 100644
index 0000000..8de9d06
--- /dev/null
+++ b/themes/Onyx/openbox-3/themerc
@@ -0,0 +1,85 @@
+!! Onyx by Dana Jansens
+!! inspired by "Carbon" by p0ng, and the ever so popular Vista glass look
+
+!! Menu background
+menu.items.bg: flat gradient vertical
+menu.items.bg.color: #303030
+menu.items.bg.colorTo: #080808
+
+!! Menu text
+menu.items.text.color: #b8b8b8
+menu.items.justify: left
+menu.items.disabled.text.color: #606060
+
+!! Menu headers
+menu.title.bg: raised splitvertical gradient
+menu.title.bg.color: #303030
+menu.title.bg.colorTo: #181818
+menu.title.text.color: white
+menu.title.text.justify: center
+
+!! Selected menu item
+menu.items.active.bg: raised splitvertical gradient
+menu.items.active.bg.color: #6d95de
+menu.items.active.bg.colorTo: #2b829d
+menu.items.active.text.color: #f8f8f8
+
+!! Titlebar
+window.active.title.bg: raised splitvertical gradient
+window.active.title.bg.color: #414141
+window.active.title.bg.colorTo: #202020
+window.inactive.title.bg: gradient splitvertical gradient flat
+window.inactive.title.bg.color: #DEE0D8
+window.inactive.title.bg.colorTo: #E0E0D8
+
+!! Titlebar text
+window.label.text.justify: center
+window.active.label.bg: parentrelative
+window.active.label.text.color: #f8f8f8
+window.inactive.label.bg: parentrelative
+window.inactive.label.text.color: #747474
+
+!! Window buttons
+window.*.button.*.bg: parentrelative
+window.active.button.*.hover.bg: flat splitvertical gradient border
+window.inactive.button.*.hover.bg: parentrelative
+window.*.button.*.pressed.bg: flat splitvertical gradient border
+
+window.active.button.*.hover.bg.color: #398dc6
+window.active.button.*.hover.bg.colorTo: #236d83
+window.active.button.*.hover.bg.border.color: #236d83
+
+window.active.button.*.pressed.bg.color: #235679
+window.active.button.*.pressed.bg.colorTo: #154350
+window.active.button.*.pressed.image.color: #898989
+window.active.button.*.pressed.bg.border.color: #154350
+window.inactive.button.*.pressed.bg.color: #4ab5ff
+window.inactive.button.*.pressed.bg.colorTo: #38b3d6
+window.inactive.button.*.pressed.image.color: #b7fdef
+window.inactive.button.*.pressed.bg.border.color: #38b3d6
+
+window.active.button.*.image.color: #e0e0e0
+window.active.button.disabled.image.color: #707070
+window.inactive.button.*.image.color: #747474
+window.inactive.button.disabled.image.color: #c0c0c0
+
+!! Borders
+window.active.border.color: #181818
+window.inactive.border.color: #404040
+window.inactive.title.separator.color: #eeeee6
+border.width: 1
+padding.width: 2
+window.handle.width: 0
+window.active.client.color: #181818
+window.inactive.client.color: #CACAB6
+window.client.padding.width: 1
+window.client.padding.height: 1
+menu.overlap: 0
+
+!! Font shadows
+menu.items.font:shadow=y:shadowtint=30
+
+!! On-screen displays
+osd.bg: gradient vertical flat
+osd.bg.color: #303030
+osd.bg.colorTo: #080808
diff --git a/themes/Orang/openbox-3/themerc b/themes/Orang/openbox-3/themerc
new file mode 100644
index 0000000..ee444c5
--- /dev/null
+++ b/themes/Orang/openbox-3/themerc
@@ -0,0 +1,100 @@
+!! i be a mess
+
+menu.title.bg: flat solid border
+menu.title.bg.color: #ce5c00
+menu.title.bg.border.color: #fcaf3e
+menu.title.text.color: #ffffff
+
+menu.items.bg: flat solid
+#menu.items.bg.color: #ffffff
+menu.items.bg.color: #d3d7cf
+menu.items.text.color: #000000
+menu.items.disabled.text.color: #737573
+
+menu.items.active.bg: flat solid
+menu.items.active.bg.color: #ce5c00
+menu.items.active.text.color: #ffffff
+
+!! General window settings
+*.justify: left
+
+!! focused window settings
+window.active.client.color: #f7f7f7
+
+window.active.title.bg: flat border solid
+window.active.title.bg.color: #ce5c00
+window.active.title.bg.border.color: #fcaf3e
+
+window.active.handle.bg: flat solid border
+window.active.handle.bg.color: #d3d7cf
+window.active.handle.bg.border.color: #eeeeec
+!window.active.handle.bg.color: #969494
+window.active.grip.bg: parentrelative
+
+window.active.label.bg: parentrelative
+window.active.label.text.color: #ffffff
+
+window.active.button.*.bg: parentrelative
+window.active.button.*.image.color: #ffffff
+
+window.active.button.pressed.bg: flat solid bevel1
+!window.active.button.pressed.bg.color: #888888
+window.active.button.pressed.bg.color: #f57900
+window.active.button.pressed.image.color: #d3d7cf
+
+!#hover
+window.active.button.hover.bg: flat solid
+window.active.button.hover.image.color: #ffffff
+window.active.button.hover.bg.color: #fcaf3e
+
+window.active.button.disabled.bg: flat solid
+window.active.button.disabled.bg.color: #dfb454
+window.active.button.disabled.image.color: #ffffff
+
+
+!! unfocused window settings
+window.inactive.client.color: #f7f7f7
+
+window.inactive.title.bg: flat solid border
+window.inactive.title.bg.color: #d3d7cf
+window.inactive.title.bg.border.color: #dfe3db
+
+window.inactive.handle.bg: flat solid
+window.inactive.handle.bg.color: #d3d7cf
+
+window.inactive.grip.bg: parentrelative
+
+window.inactive.label.bg: parentrelative
+window.inactive.label.text.color: #888a85
+
+window.inactive.button.*.bg: parentrelative
+window.inactive.button.unpressed.image.color: #555555
+
+window.inactive.button.pressed.bg: flat border solid
+window.inactive.button.pressed.bg.color: #aaaaaa
+window.inactive.button.pressed.bg.border.color: #e5e5e5
+
+window.inactive.button.hover.bg: flat solid
+window.inactive.button.hover.bg.color: #e3e3e3
+
+window.inactive.button.disabled.bg:flat solid
+window.inactive.button.disabled.bg.color: #e3e3e3
+
+
+!! Global width settings
+border.Width: 1
+padding.width: 1
+window.handle.width: 3
+window.client.padding.width: 0
+window.client.padding.height: 0
+menu.overlap: 2
+
+!! Miscellaneous settings
+border.color: #000000
+
+!! font me!
+window.active.label.text.font:
+window.inactive.label.text.font:
+menu.title.text.font:
+menu.items.font:
+
diff --git a/themes/Syscrash/openbox-3/max.xbm b/themes/Syscrash/openbox-3/max.xbm
new file mode 100644
index 0000000..44c7cef
--- /dev/null
+++ b/themes/Syscrash/openbox-3/max.xbm
@@ -0,0 +1,4 @@
+#define max_width 6
+#define max_height 6
+static unsigned char max_bits[] = {
+ 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f };
diff --git a/themes/Syscrash/openbox-3/max_disabled.xbm b/themes/Syscrash/openbox-3/max_disabled.xbm
new file mode 100644
index 0000000..6d030af
--- /dev/null
+++ b/themes/Syscrash/openbox-3/max_disabled.xbm
@@ -0,0 +1,4 @@
+#define max_width 6
+#define max_height 6
+static unsigned char max_bits[] = {
+ 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f };
diff --git a/themes/Syscrash/openbox-3/max_pressed.xbm b/themes/Syscrash/openbox-3/max_pressed.xbm
new file mode 100644
index 0000000..6d030af
--- /dev/null
+++ b/themes/Syscrash/openbox-3/max_pressed.xbm
@@ -0,0 +1,4 @@
+#define max_width 6
+#define max_height 6
+static unsigned char max_bits[] = {
+ 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f };
diff --git a/themes/Syscrash/openbox-3/max_toggled.xbm b/themes/Syscrash/openbox-3/max_toggled.xbm
new file mode 100644
index 0000000..020f779
--- /dev/null
+++ b/themes/Syscrash/openbox-3/max_toggled.xbm
@@ -0,0 +1,4 @@
+#define max_toggled_width 6
+#define max_toggled_height 6
+static unsigned char max_toggled_bits[] = {
+ 0x3c, 0x27, 0x25, 0x3d, 0x11, 0x1f };
diff --git a/themes/Syscrash/openbox-3/themerc b/themes/Syscrash/openbox-3/themerc
new file mode 100644
index 0000000..65666d9
--- /dev/null
+++ b/themes/Syscrash/openbox-3/themerc
@@ -0,0 +1,114 @@
+!! syscrash's theme, based off nightm4re's flax or something
+
+!!---------------------------------------------------------------------------
+!! Dimensions
+
+border.width: 1
+border.color: #6d6d6d
+
+padding.width: 1
+window.handle.width: 0
+window.client.padding.width: 0
+window.client.padding.height: 0
+
+menu.overlap: 0
+
+!!---------------------------------------------------------------------------
+!! Fonts
+
+window.active.label.text.font: shadow=y
+window.label.text.justify: left
+
+menu.title.text.font: shadow=y
+menu.title.text.justify: left
+
+menu.items.font:
+
+!!---------------------------------------------------------------------------
+!! Menu Settings
+
+menu.title.bg: flat gradient vertical
+menu.title.bg.color: #4c4c4c
+menu.title.bg.colorTo: #707070
+menu.title.text.color: white
+
+menu.items.bg: flat solid
+menu.items.bg.color: #e6e6e0
+menu.items.text.color: #22221c
+
+menu.items.active.bg: flat solid
+menu.items.active.bg.color: #4c4c4c
+menu.items.active.text.color: #ffffff
+
+menu.items.disabled.text.color: #8c8c75
+
+!!---------------------------------------------------------------------------
+!! Window Settings
+
+window.active.padding.width: 10
+window.active.title.bg: flat solid
+window.active.title.bg.color: #9b9b9b
+
+window.active.label.bg: flat gradient vertical border
+window.active.label.bg.color: #4c4c4c
+window.active.label.bg.colorTo: #707070
+window.active.label.text.color: white
+
+window.active.handle.bg: flat gradient crossdiagonal
+window.active.handle.bg.color: #d3deda
+window.active.handle.bg.colorTo: #9fbfc1
+
+window.active.grip.bg: flat solid
+window.active.grip.bg.color: #9fbfc1
+
+window.active.client.color: #4c4c4c
+window.inactive.client.color: #707070
+
+window.inactive.title.bg: flat solid
+window.inactive.title.bg.color: #9b9b9b
+
+window.inactive.label.bg: parentrelative
+window.inactive.label.text.color: #4c4c4c
+
+window.inactive.handle.bg: flat solid
+window.inactive.handle.bg.color: #e6e6e0
+
+window.inactive.grip.bg: flat solid
+window.inactive.grip.bg.color: #e6e6e0
+
+!!---------------------------------------------------------------------------
+!! Button Settings
+
+window.active.button.unpressed.bg: flat gradient vertical border
+window.active.button.unpressed.bg.color: #515151
+window.active.button.unpressed.bg.colorTo: #676767
+window.active.button.unpressed.image.color: white
+
+window.active.button.pressed.bg: flat gradient crossdiagonal border
+window.active.button.pressed.bg.color: #d3deda
+window.active.button.pressed.bg.colorTo: #9fbfc1
+
+window.active.button.hover.bg: flat solid border
+window.active.button.hover.bg.color: #e6e6e0
+
+window.active.button.disabled.bg: parentrelative
+window.active.button.disabled.image.color: #8c8c75
+
+!! without this it looks right
+!!window.active.button.toggled.bg: flat solid
+!!window.active.button.toggled.bg.color: #9fbfc1
+
+window.inactive.button.unpressed.bg: parentrelative
+window.inactive.button.unpressed.image.color: #4c4c4c
+
+window.inactive.button.hover.bg: flat solid border
+window.inactive.button.hover.bg.color: #e6e6e0
+window.inactive.button.pressed.bg: flat gradient crossdiagonal border
+window.inactive.button.pressed.bg.color: #d3deda
+window.inactive.button.pressed.bg.colorTo: #9fbfc1
+
+window.inactive.button.disabled.bg: parentrelative
+window.inactive.button.disabled.image.color: #8c8c75
+
+!! window.inactive.button.toggled.bg: flat solid
+!! window.inactive.button.toggled.bg.color: #444438
diff --git a/tools/gdm-control/Makefile b/tools/gdm-control/Makefile
new file mode 100644
index 0000000..cfc4653
--- /dev/null
+++ b/tools/gdm-control/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C ../.. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/tools/gdm-control/gdm-control.c b/tools/gdm-control/gdm-control.c
new file mode 100644
index 0000000..db28841
--- /dev/null
+++ b/tools/gdm-control/gdm-control.c
@@ -0,0 +1,295 @@
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xauth.h>
+
+#include <glib.h>
+
+typedef enum
+{
+ INVALID,
+ NONE,
+ SHUTDOWN,
+ REBOOT,
+ SUSPEND,
+ SWITCHUSER
+} Action;
+
+#define GDM_PROTOCOL_SOCKET_PATH1 "/var/run/gdm_socket"
+#define GDM_PROTOCOL_SOCKET_PATH2 "/tmp/.gdm_socket"
+
+#define GDM_PROTOCOL_MSG_CLOSE "CLOSE"
+#define GDM_PROTOCOL_MSG_VERSION "VERSION"
+#define GDM_PROTOCOL_MSG_AUTHENTICATE "AUTH_LOCAL"
+#define GDM_PROTOCOL_MSG_QUERY_ACTION "QUERY_LOGOUT_ACTION"
+#define GDM_PROTOCOL_MSG_SET_ACTION "SET_SAFE_LOGOUT_ACTION"
+#define GDM_PROTOCOL_MSG_FLEXI_XSERVER "FLEXI_XSERVER"
+
+#define GDM_ACTION_STR_NONE GDM_PROTOCOL_MSG_SET_ACTION" NONE"
+#define GDM_ACTION_STR_SHUTDOWN GDM_PROTOCOL_MSG_SET_ACTION" HALT"
+#define GDM_ACTION_STR_REBOOT GDM_PROTOCOL_MSG_SET_ACTION" REBOOT"
+#define GDM_ACTION_STR_SUSPEND GDM_PROTOCOL_MSG_SET_ACTION" SUSPEND"
+
+#define GDM_MIT_MAGIC_COOKIE_LEN 16
+
+static int fd = 0;
+
+static void gdm_disconnect()
+{
+ if (fd > 0)
+ close(fd);
+ fd = 0;
+}
+
+static char* get_display_number(void)
+{
+ char *display_name;
+ char *retval;
+ char *p;
+
+ display_name = XDisplayName(NULL);
+
+ p = strchr(display_name, ':');
+ if (!p)
+ return g_strdup ("0");
+
+ while (*p == ':') p++;
+
+ retval = g_strdup (p);
+
+ p = strchr (retval, '.');
+ if (p != NULL)
+ *p = '\0';
+
+ return retval;
+}
+
+static char* gdm_send_protocol_msg (const char *msg)
+{
+ GString *retval;
+ char buf[256];
+ char *p;
+ int len;
+
+ p = g_strconcat(msg, "\n", NULL);
+ if (write (fd, p, strlen(p)) < 0) {
+ g_free (p);
+
+ g_warning ("Failed to send message to GDM: %s",
+ g_strerror (errno));
+ return NULL;
+ }
+ g_free (p);
+
+ p = NULL;
+ retval = NULL;
+ while ((len = read(fd, buf, sizeof(buf) - 1)) > 0) {
+ buf[len] = '\0';
+
+ if (!retval)
+ retval = g_string_new(buf);
+ else
+ retval = g_string_append(retval, buf);
+
+ if ((p = strchr(retval->str, '\n')))
+ break;
+ }
+
+ if (p) *p = '\0';
+
+ return retval ? g_string_free(retval, FALSE) : NULL;
+}
+
+static gboolean gdm_authenticate()
+{
+ FILE *f;
+ Xauth *xau;
+ const char *xau_path;
+ char *display_number;
+ gboolean retval;
+
+ if (!(xau_path = XauFileName()))
+ return FALSE;
+
+ if (!(f = fopen(xau_path, "r")))
+ return FALSE;
+
+ retval = FALSE;
+ display_number = get_display_number();
+
+ while ((xau = XauReadAuth(f))) {
+ char buffer[40]; /* 2*16 == 32, so 40 is enough */
+ char *msg;
+ char *response;
+ int i;
+
+ if (xau->family != FamilyLocal ||
+ strncmp (xau->number, display_number, xau->number_length) ||
+ strncmp (xau->name, "MIT-MAGIC-COOKIE-1", xau->name_length) ||
+ xau->data_length != GDM_MIT_MAGIC_COOKIE_LEN)
+ {
+ XauDisposeAuth(xau);
+ continue;
+ }
+
+ for (i = 0; i < GDM_MIT_MAGIC_COOKIE_LEN; i++)
+ g_snprintf(buffer + 2*i, 3, "%02x", (guint)(guchar)xau->data[i]);
+
+ XauDisposeAuth(xau);
+
+ msg = g_strdup_printf(GDM_PROTOCOL_MSG_AUTHENTICATE " %s", buffer);
+ response = gdm_send_protocol_msg(msg);
+ g_free (msg);
+
+ if (response && !strcmp(response, "OK")) {
+ /*auth_cookie = g_strdup(buffer);*/
+ g_free(response);
+ retval = TRUE;
+ break;
+ }
+
+ g_free (response);
+ }
+
+ fclose(f);
+ return retval;
+}
+
+static gboolean gdm_connect()
+{
+ struct sockaddr_un addr;
+ char *response;
+
+ assert(fd <= 0);
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ g_warning("Failed to create GDM socket: %s", g_strerror (errno));
+ gdm_disconnect();
+ return FALSE;
+ }
+
+ if (g_file_test(GDM_PROTOCOL_SOCKET_PATH1, G_FILE_TEST_EXISTS))
+ strcpy(addr.sun_path, GDM_PROTOCOL_SOCKET_PATH1);
+ else
+ strcpy(addr.sun_path, GDM_PROTOCOL_SOCKET_PATH2);
+
+ addr.sun_family = AF_UNIX;
+
+ if (connect(fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
+ g_warning("Failed to establish a connection with GDM: %s",
+ g_strerror(errno));
+ gdm_disconnect();
+ return FALSE;
+ }
+
+ response = gdm_send_protocol_msg(GDM_PROTOCOL_MSG_VERSION);
+ if (!response || strncmp(response, "GDM ", strlen("GDM ") != 0)) {
+ g_free(response);
+
+ g_warning("Failed to get protocol version from GDM");
+ gdm_disconnect();
+ return FALSE;
+ }
+ g_free(response);
+
+ if (!gdm_authenticate()) {
+ g_warning("Failed to authenticate with GDM");
+ gdm_disconnect();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int main(int argc, char **argv)
+{
+ int i;
+ Action a = INVALID;
+
+ for (i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "--help")) {
+ a = INVALID;
+ break;
+ }
+ if (!strcmp(argv[i], "--none")) {
+ a = NONE;
+ break;
+ }
+ if (!strcmp(argv[i], "--shutdown")) {
+ a = SHUTDOWN;
+ break;
+ }
+ if (!strcmp(argv[i], "--reboot")) {
+ a = REBOOT;
+ break;
+ }
+ if (!strcmp(argv[i], "--suspend")) {
+ a = SUSPEND;
+ break;
+ }
+ if (!strcmp(argv[i], "--switch-user")) {
+ a = SWITCHUSER;
+ break;
+ }
+ }
+
+ if (!a) {
+ printf("Usage: gdm-control ACTION\n\n");
+ printf("Actions:\n");
+ printf(" --help Display this help and exit\n");
+ printf(" --none Do nothing special when the current session ends\n");
+ printf(" --shutdown Shutdown the computer when the current session ends\n");
+ printf(" --reboot Reboot the computer when the current session ends\n");
+ printf(" --suspend Suspend the computer when the current session ends\n");
+ printf(" --switch-user Log in as a new user (this works immediately)\n\n");
+ return 0;
+ }
+
+ {
+ char *d, *response;
+ const char *action_string;
+
+ d = XDisplayName(NULL);
+ if (!d) {
+ fprintf(stderr,
+ "Unable to find the X display specified by the DISPLAY "
+ "environment variable. Ensure that it is set correctly.");
+ return 1;
+ }
+
+ switch (a) {
+ case NONE:
+ action_string = GDM_ACTION_STR_NONE;
+ break;
+ case SHUTDOWN:
+ action_string = GDM_ACTION_STR_SHUTDOWN;
+ break;
+ case REBOOT:
+ action_string = GDM_ACTION_STR_REBOOT;
+ break;
+ case SUSPEND:
+ action_string = GDM_ACTION_STR_SUSPEND;
+ break;
+ case SWITCHUSER:
+ action_string = GDM_PROTOCOL_MSG_FLEXI_XSERVER;
+ break;
+ default:
+ assert(0);
+ }
+
+ if (gdm_connect()) {
+ response = gdm_send_protocol_msg(action_string);
+ g_free(response);
+ gdm_disconnect();
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/gnome-panel-control/Makefile b/tools/gnome-panel-control/Makefile
new file mode 100644
index 0000000..cfc4653
--- /dev/null
+++ b/tools/gnome-panel-control/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C ../.. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/tools/gnome-panel-control/gnome-panel-control.c b/tools/gnome-panel-control/gnome-panel-control.c
new file mode 100644
index 0000000..67aa039
--- /dev/null
+++ b/tools/gnome-panel-control/gnome-panel-control.c
@@ -0,0 +1,100 @@
+#include <X11/Xlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+typedef enum
+{
+ NONE,
+ MAIN_MENU,
+ RUN_DIALOG
+} Action;
+
+int main(int argc, char **argv)
+{
+ int i;
+ Action a = NONE;
+
+ for (i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "--help")) {
+ a = NONE;
+ break;
+ }
+ if (!strcmp(argv[i], "--main-menu")) {
+ a = MAIN_MENU;
+ break;
+ }
+ if (!strcmp(argv[i], "--run-dialog")) {
+ a = RUN_DIALOG;
+ break;
+ }
+ }
+
+ if (!a) {
+ printf("Usage: gnome-panel-control ACTION\n\n");
+ printf("Actions:\n");
+ printf(" --help Display this help and exit\n");
+ printf(" --main-menu Show the main menu\n");
+ printf(" --run-dialog Show the run dialog\n\n");
+ return 0;
+ }
+
+ {
+ Display *d;
+ Window root;
+ XClientMessageEvent ce;
+ Atom act_atom;
+ Time timestamp;
+
+ d = XOpenDisplay(NULL);
+ if (!d) {
+ fprintf(stderr,
+ "Unable to open the X display specified by the DISPLAY "
+ "environment variable. Ensure you have permission to "
+ "connect to the display.");
+ return 1;
+ }
+ root = RootWindowOfScreen(DefaultScreenOfDisplay(d));
+
+ switch (a) {
+ case MAIN_MENU:
+ act_atom = XInternAtom(d, "_GNOME_PANEL_ACTION_MAIN_MENU", False);
+ break;
+ case RUN_DIALOG:
+ act_atom = XInternAtom(d, "_GNOME_PANEL_ACTION_RUN_DIALOG", False);
+ break;
+ default:
+ assert(0);
+ }
+
+ /* Generate a timestamp */
+ {
+ XEvent event;
+ Window win;
+
+ win = XCreateSimpleWindow(d, root, 0, 0, 1, 1, 0, 0, 0);
+
+ XSelectInput(d, win, PropertyChangeMask);
+
+ XChangeProperty(d, win, act_atom, act_atom, 8,
+ PropModeAppend, NULL, 0);
+ XWindowEvent(d, win, PropertyChangeMask, &event);
+
+ XDestroyWindow(d, win);
+
+ timestamp = event.xproperty.time;
+ }
+
+ ce.type = ClientMessage;
+ ce.window = root;
+ ce.message_type = XInternAtom(d, "_GNOME_PANEL_ACTION", False);
+ ce.format = 32;
+ ce.data.l[0] = act_atom;
+ ce.data.l[1] = timestamp;
+ XSendEvent(d, root, False, StructureNotifyMask, (XEvent*) &ce);
+
+ XCloseDisplay(d);
+ }
+
+ return 0;
+}
diff --git a/tools/obxprop/Makefile b/tools/obxprop/Makefile
new file mode 100644
index 0000000..cfc4653
--- /dev/null
+++ b/tools/obxprop/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C ../.. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/tools/obxprop/obxprop.c b/tools/obxprop/obxprop.c
new file mode 100644
index 0000000..83ff54a
--- /dev/null
+++ b/tools/obxprop/obxprop.c
@@ -0,0 +1,351 @@
+#include <X11/Xlib.h>
+#include <X11/cursorfont.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <glib.h>
+
+gint fail(const gchar *s) {
+ if (s)
+ fprintf(stderr, "%s\n", s);
+ else
+ fprintf
+ (stderr,
+ "Usage: obxprop [OPTIONS] [--] [PROPERTIES ...]\n\n"
+ "Options:\n"
+ " --help Display this help and exit\n"
+ " --display DISPLAY Connect to this X display\n"
+ " --id ID Show the properties for this window\n"
+ " --root Show the properties for the root window\n");
+ return 1;
+}
+
+gint parse_hex(gchar *s) {
+ gint result = 0;
+ while (*s) {
+ gint add;
+ if (*s >= '0' && *s <='9')
+ add = *s-'0';
+ else if (*s >= 'A' && *s <='F')
+ add = *s-'A'+10;
+ else if (*s >= 'a' && *s <='f')
+ add = *s-'a'+10;
+ else
+ break;
+
+ result *= 16;
+ result += add;
+ ++s;
+ }
+ return result;
+}
+
+Window find_client(Display *d, Window win)
+{
+ Window r, *children;
+ guint n, i;
+ Atom state = XInternAtom(d, "WM_STATE", True);
+ Atom ret_type;
+ gint ret_format, res;
+ gulong ret_items, ret_bytesleft, *xdata;
+
+ XQueryTree(d, win, &r, &r, &children, &n);
+ for (i = 0; i < n; ++i) {
+ Window w = find_client(d, children[i]);
+ if (w) return w;
+ }
+
+ // try me
+ res = XGetWindowProperty(d, win, state, 0, 1,
+ False, state, &ret_type, &ret_format,
+ &ret_items, &ret_bytesleft,
+ (unsigned char**) &xdata);
+ XFree(xdata);
+ if (res != Success || ret_type == None || ret_items < 1)
+ return None;
+ return win; // found it!
+}
+
+static gboolean get_all(Display *d, Window win, Atom prop,
+ Atom *type, gint *size,
+ guchar **data, guint *num)
+{
+ gboolean ret = FALSE;
+ gint res;
+ guchar *xdata = NULL;
+ gulong ret_items, bytes_left;
+
+ res = XGetWindowProperty(d, win, prop, 0l, G_MAXLONG,
+ FALSE, AnyPropertyType, type, size,
+ &ret_items, &bytes_left, &xdata);
+ if (res == Success) {
+ if (ret_items > 0) {
+ guint i;
+
+ *data = g_malloc(ret_items * (*size / 8));
+ for (i = 0; i < ret_items; ++i)
+ switch (*size) {
+ case 8:
+ (*data)[i] = xdata[i];
+ break;
+ case 16:
+ ((guint16*)*data)[i] = ((gushort*)xdata)[i];
+ break;
+ case 32:
+ ((guint32*)*data)[i] = ((gulong*)xdata)[i];
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled size */
+ }
+ }
+ *num = ret_items;
+ ret = TRUE;
+ XFree(xdata);
+ }
+ return ret;
+}
+
+gchar *append_string(gchar *before, gchar *after, gboolean quote)
+{
+ gchar *tmp;
+ const gchar *q = quote ? "\"" : "";
+ if (before)
+ tmp = g_strdup_printf("%s, %s%s%s", before, q, after, q);
+ else
+ tmp = g_strdup_printf("%s%s%s", q, after, q);
+ g_free(before);
+ return tmp;
+}
+
+gchar *append_int(gchar *before, guint after)
+{
+ gchar *tmp;
+ if (before)
+ tmp = g_strdup_printf("%s, %u", before, after);
+ else
+ tmp = g_strdup_printf("%u", after);
+ g_free(before);
+ return tmp;
+}
+
+gchar* read_strings(gchar *val, guint n, gboolean utf8)
+{
+ GSList *strs = NULL, *it;
+ gchar *ret, *p;
+ guint i;
+
+ p = val;
+ while (p < val + n) {
+ strs = g_slist_append(strs, g_strndup(p, n - (p - val)));
+ p += strlen(p) + 1; /* next string */
+ }
+
+ ret = NULL;
+ for (i = 0, it = strs; it; ++i, it = g_slist_next(it)) {
+ char *data;
+
+ if (utf8) {
+ if (g_utf8_validate(it->data, -1, NULL))
+ data = g_strdup(it->data);
+ else
+ data = g_strdup("");
+ }
+ else
+ data = g_locale_to_utf8(it->data, -1, NULL, NULL, NULL);
+
+ ret = append_string(ret, data, TRUE);
+ g_free(data);
+ }
+
+ while (strs) {
+ g_free(strs->data);
+ strs = g_slist_delete_link(strs, strs);
+ }
+ return ret;
+}
+
+gchar* read_atoms(Display *d, guchar *val, guint n)
+{
+ gchar *ret;
+ guint i;
+
+ ret = NULL;
+ for (i = 0; i < n; ++i)
+ ret = append_string(ret, XGetAtomName(d, ((guint32*)val)[i]), FALSE);
+ return ret;
+}
+
+gchar* read_numbers(guchar *val, guint n, guint size)
+{
+ gchar *ret;
+ guint i;
+
+ ret = NULL;
+ for (i = 0; i < n; ++i)
+ switch (size) {
+ case 8:
+ ret = append_int(ret, ((guint8*)val)[i]);
+ break;
+ case 16:
+ ret = append_int(ret, ((guint16*)val)[i]);
+ break;
+ case 32:
+ ret = append_int(ret, ((guint32*)val)[i]);
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled size */
+ }
+
+ return ret;
+}
+
+gboolean read_prop(Display *d, Window w, Atom prop, const gchar **type, gchar **val)
+{
+ guchar *ret;
+ guint nret;
+ gint size;
+ Atom ret_type;
+
+ ret = NULL;
+ if (get_all(d, w, prop, &ret_type, &size, &ret, &nret)) {
+ *type = XGetAtomName(d, ret_type);
+
+ if (strcmp(*type, "STRING") == 0)
+ *val = read_strings((gchar*)ret, nret, FALSE);
+ else if (strcmp(*type, "UTF8_STRING") == 0)
+ *val = read_strings((gchar*)ret, nret, TRUE);
+ else if (strcmp(*type, "ATOM") == 0) {
+ g_assert(size == 32);
+ *val = read_atoms(d, ret, nret);
+ }
+ else
+ *val = read_numbers(ret, nret, size);
+
+ g_free(ret);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void show_properties(Display *d, Window w, int argc, char **argv)
+{
+ Atom* props;
+ int i, n;
+
+ props = XListProperties(d, w, &n);
+
+ for (i = 0; i < n; ++i) {
+ const char *type;
+ char *name, *val;
+
+ name = XGetAtomName(d, props[i]);
+
+ if (read_prop(d, w, props[i], &type, &val)) {
+ int found = 1;
+ if (argc) {
+ int i;
+
+ found = 0;
+ for (i = 0; i < argc; i++)
+ if (!strcmp(name, argv[i])) {
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ g_print("%s(%s) = %s\n", name, type, (val ? val : ""));
+ g_free(val);
+ }
+
+ XFree(name);
+ }
+
+ XFree(props);
+}
+
+int main(int argc, char **argv)
+{
+ Display *d;
+ Window id, userid = None;
+ int i;
+ char *dname = NULL;
+ gboolean root = FALSE;
+
+ for (i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "--help")) {
+ return fail(NULL);
+ }
+ else if (!strcmp(argv[i], "--root"))
+ root = TRUE;
+ else if (!strcmp(argv[i], "--id")) {
+ if (++i == argc)
+ return fail(NULL);
+ if (argv[i][0] == '0' && argv[i][1] == 'x') {
+ /* hex */
+ userid = parse_hex(argv[i]+2);
+ }
+ else {
+ /* decimal */
+ userid = atoi(argv[i]);
+ }
+ if (!userid)
+ return fail("Unable to parse argument to --id.");
+ }
+ else if (!strcmp(argv[i], "--display")) {
+ if (++i == argc)
+ return fail(NULL);
+ dname = argv[i];
+ }
+ else if (*argv[i] != '-')
+ break;
+ else if (!strcmp(argv[i], "--")) {
+ i++;
+ break;
+ }
+ else
+ return fail(NULL);
+ }
+
+ d = XOpenDisplay(dname);
+ if (!d) {
+ return fail("Unable to find an X display. "
+ "Ensure you have permission to connect to the display.");
+ }
+
+ if (root)
+ userid = RootWindow(d, DefaultScreen(d));
+
+ if (userid == None) {
+ int j;
+ j = XGrabPointer(d, RootWindow(d, DefaultScreen(d)),
+ False, ButtonPressMask,
+ GrabModeAsync, GrabModeAsync,
+ None, XCreateFontCursor(d, XC_crosshair),
+ CurrentTime);
+ if (j != GrabSuccess)
+ return fail("Unable to grab the pointer device");
+ while (1) {
+ XEvent ev;
+
+ XNextEvent(d, &ev);
+ if (ev.type == ButtonPress) {
+ XUngrabPointer(d, CurrentTime);
+ userid = ev.xbutton.subwindow;
+ break;
+ }
+ }
+ id = find_client(d, userid);
+ }
+ else
+ id = userid; /* they picked this one */
+
+ if (id == None)
+ return fail("Unable to find window with the requested ID");
+
+ show_properties(d, id, argc - i, &argv[i]);
+
+ XCloseDisplay(d);
+
+ return 0;
+}
diff --git a/tools/themeupdate/themeupdate.py b/tools/themeupdate/themeupdate.py
new file mode 100755
index 0000000..c2ad6a7
--- /dev/null
+++ b/tools/themeupdate/themeupdate.py
@@ -0,0 +1,410 @@
+#! /usr/bin/python
+
+# themeupdate.py for the Openbox window manager
+# This utility is for updating old themes from Blackbox, Fluxbox, and Openbox2
+# to Openbox3
+#
+# Copyright (c) 2003-2007 Dana Jansens
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# See the COPYING file for a copy of the GNU General Public License.
+
+import sys
+
+data = []
+valid = True
+
+def out(str):
+ sys.stderr.write(str)
+ sys.stderr.flush()
+
+def read_bool():
+ while True:
+ inp = sys.stdin.readline(1).strip()
+ if inp == 'y' or inp == '': return True
+ if inp == 'n': return False
+
+def getkeyval(line):
+ key = line[:line.find(':')].strip()
+ value = line[line.find(':') + 1:].strip()
+ if not (key and value):
+ key = value = None
+ return key, value
+
+def find_key(data, keysubstr, exact = False):
+ i = 0
+ n = len(data)
+ while i < n:
+ l = data[i]
+ key, value = getkeyval(l)
+ if key and value:
+ if (exact and key == keysubstr) or \
+ (not exact and key.find(keysubstr) != -1):
+ return i, key, value
+ i += 1
+ return -1, None, None
+
+def simple_replace(data):
+ pairs = {}
+ pairs['.picColor'] = '.imageColor'
+ pairs['menu.frame'] = 'menu.items'
+ pairs['menu.hilite'] = 'menu.selected'
+ pairs['borderColor'] = 'border.color'
+ pairs['imageColor'] = 'image.color'
+ pairs['textColor'] = 'text.color'
+ pairs['interlaceColor'] = 'interlace.color'
+
+ for k in pairs.keys():
+ while 1:
+ i, key, nul = find_key(data, k);
+ if i >= 0:
+ newl = data[i].replace(k, pairs[k])
+ out('Updating "' + key +
+ '" to "' + key.replace(k, pairs[k]) + '"\n')
+ data[i] = newl
+ else:
+ break
+
+ pairs = {}
+ pairs['window.focus.font'] = 'window.active.label.text.font'
+ pairs['window.unfocus.font'] = 'window.inactive.label.text.font'
+ pairs['window.justify'] = 'window.label.justify'
+ pairs['menu.frame.disableColor'] = 'menu.disabled.textColor'
+ pairs['window.label.focus.font'] = 'window.active.label.text.font'
+ pairs['window.label.unfocus.font'] = 'window.inactive.label.text.font'
+ pairs['window.label.justify'] = 'window.label.text.justify'
+ pairs['menu.title.font'] = 'menu.title.text.font'
+ pairs['menu.title.justify'] = 'menu.title.text.justify'
+ pairs['menuOverlap'] = 'menu.overlap'
+ pairs['handleWidth'] = 'window.handle.width'
+ pairs['borderWidth'] = 'border.width'
+ pairs['bevelWidth'] = 'padding.width'
+ pairs['frameWidth'] = 'window.client.padding.width'
+ pairs['window.frame.focusColor'] = 'window.active.client.color'
+ pairs['window.frame.unfocusColor'] = 'window.inactive.client.color'
+ pairs['window.title.focus'] = 'window.active.title.bg'
+ pairs['window.title.unfocus'] = 'window.inactive.title.bg'
+ pairs['window.label.focus'] = 'window.active.label.bg'
+ pairs['window.label.unfocus'] = 'window.inactive.label.bg'
+ pairs['window.handle.focus'] = 'window.active.handle.bg'
+ pairs['window.handle.unfocus'] = 'window.inactive.handle.bg'
+ pairs['window.grip.focus'] = 'window.active.grip.bg'
+ pairs['window.grip.unfocus'] = 'window.inactive.grip.bg'
+ pairs['menu.items'] = 'menu.items.bg'
+ pairs['menu.title'] = 'menu.title.bg'
+ pairs['menu.selected'] = 'menu.items.active.bg'
+ pairs['window.title.focus'] = 'window.active.title.bg'
+ pairs['window.label.focus'] = 'window.active.label.bg'
+ pairs['window.title.unfocus'] = 'window.inactive.title.bg'
+ pairs['window.label.unfocus'] = 'window.inactive.label.bg'
+ pairs['window.button.disabled.focus'] = 'window.active.button.disabled.bg'
+ pairs['window.button.disabled.unfocus'] = \
+ 'window.inactive.button.disabled.bg'
+ pairs['window.button.pressed.focus'] = 'window.active.button.pressed.bg'
+ pairs['window.button.pressed.unfocus'] = \
+ 'window.inactive.button.pressed.bg'
+ pairs['window.button.toggled.focus'] = 'window.active.button.toggled.bg'
+ pairs['window.button.toggled.unfocus'] = \
+ 'window.inactive.button.toggled.bg'
+ pairs['window.button.focus'] = 'window.active.button.unpressed.bg'
+ pairs['window.button.unfocus'] = 'window.inactive.button.unpressed.bg'
+ pairs['window.button.hover.focus'] = 'window.active.button.hover.bg'
+ pairs['window.button.hover.unfocus'] = 'window.inactive.button.hover.bg'
+
+ for k in pairs.keys():
+ while 1:
+ i, key, nul = find_key(data, k, True);
+ if i >= 0:
+ newl = data[i].replace(k, pairs[k])
+ out('Updating "' + key +
+ '" to "' + key.replace(k, pairs[k]) + '"\n')
+ data[i] = newl
+ else:
+ break
+
+ pairs = {}
+ pairs['window.title.focus'] = 'window.active.title'
+ pairs['window.title.unfocus'] = 'window.inactive.title'
+ pairs['window.label.focus'] = 'window.active.label'
+ pairs['window.label.unfocus'] = 'window.inactive.label'
+ pairs['window.handle.focus'] = 'window.active.handle'
+ pairs['window.handle.unfocus'] = 'window.inactive.handle'
+ pairs['window.grip.focus'] = 'window.active.grip'
+ pairs['window.grip.unfocus'] = 'window.inactive.grip'
+ pairs['menu.selected'] = 'menu.items.active'
+ pairs['window.title.focus'] = 'window.active.title'
+ pairs['window.label.focus'] = 'window.active.label'
+ pairs['window.title.unfocus'] = 'window.inactive.title'
+ pairs['window.label.unfocus'] = 'window.inactive.label'
+ pairs['window.button.disabled.focus'] = 'window.active.button.disabled'
+ pairs['window.button.disabled.unfocus'] = \
+ 'window.inactive.button.disabled'
+ pairs['window.button.pressed.focus'] = 'window.active.button.pressed'
+ pairs['window.button.pressed.unfocus'] = \
+ 'window.inactive.button.pressed'
+ pairs['window.button.toggled.focus'] = 'window.active.button.toggled'
+ pairs['window.button.toggled.unfocus'] = \
+ 'window.inactive.button.toggled'
+ pairs['window.button.focus'] = 'window.active.button'
+ pairs['window.button.unfocus'] = 'window.inactive.button'
+ pairs['window.button.hover.focus'] = 'window.active.button.hover'
+ pairs['window.button.hover.unfocus'] = 'window.inactive.button.hover'
+ pairs['window.label.unfocus'] = 'window.inactive.label'
+ pairs['window.label.focus'] = 'window.active.label'
+ pairs['window.button.focus'] = 'window.active.button.unpressed'
+ pairs['menu.disabled'] = 'menu.items.disabled'
+ pairs['menu.selected'] = 'menu.items.active'
+ pairs['window.button.unfocus'] = 'window.inactive.button.unpressed'
+ pairs['window.button.pressed.focus'] = 'window.active.button.pressed'
+ pairs['window.button.pressed.unfocus'] = 'window.inactive.button.pressed'
+ pairs['window.button.disabled.focus'] = 'window.active.button.disabled'
+ pairs['window.button.disabled.unfocus'] = 'window.inactive.button.disabled'
+ pairs['window.button.hover.focus'] = 'window.active.button.hover'
+ pairs['window.button.hover.unfocus'] = 'window.inactive.button.hover'
+ pairs['window.button.toggled.focus'] = 'window.active.button.toggled'
+ pairs['window.button.toggled.unfocus'] = 'window.inactive.button.toggled'
+
+ for k in pairs.keys():
+ while 1:
+ i, key, nul = find_key(data, k);
+ if i >= 0:
+ newl = data[i].replace(k, pairs[k])
+ out('Updating "' + key +
+ '" to "' + key.replace(k, pairs[k]) + '"\n')
+ data[i] = newl
+ else:
+ break
+
+def replace_colors(data):
+ i = 0
+ n = len(data)
+ while i < n:
+ l = data[i]
+ key, value = getkeyval(l)
+ if key and value:
+ if key.find('.color') != -1:
+ if key.find('client.color') == -1 \
+ and key.find('image.color') == -1 \
+ and key.find('bg.color') == -1 \
+ and key.find('border.color') == -1 \
+ and key.find('interlace.color') == -1 \
+ and key.find('text.color') == -1:
+ newl = data[i].replace('.color', '.bg.color')
+ out('Updating "' + key +
+ '" to "' + key.replace('.color', '.bg.color') + '"\n')
+ data[i] = newl
+ if key.find('.border.color') != -1 \
+ and key.find('bg.border.color') == -1:
+ newl = data[i].replace('.border.color', '.bg.border.color')
+ out('Updating "' + key +
+ '" to "' + key.replace('.border.color',
+ '.bg.border.color') + '"\n')
+ data[i] = newl
+ if key.find('.interlace.color') != -1 \
+ and key.find('bg.interlace.color') == -1:
+ newl = data[i].replace('.interlace.color',
+ '.bg.interlace.color')
+ out('Updating "' + key +
+ '" to "' + key.replace('.interlace.color',
+ '.bg.interlace.color') + '"\n')
+ data[i] = newl
+ i += 1
+
+def remove(data):
+ invalid = []
+ invalid.append('toolbar')
+ for inv in invalid:
+ while 1:
+ i, key, nul = find_key(data, inv)
+ if i >= 0:
+ out(key + ' is no longer supported.\nRemove (Y/n)? ')
+ if read_bool():
+ out('Removing "' + key + '"\n')
+ data.pop(i)
+ else:
+ break
+ invalid.append('rootCommand')
+ invalid.append('menu.bullet')
+ invalid.append('menu.bullet.image.color')
+ invalid.append('menu.bullet.selected.image.color')
+ invalid.append('menu.frame.justify')
+ for inv in invalid:
+ while 1:
+ i, key, nul = find_key(data, inv, True)
+ if i >= 0:
+ out(key + ' is no longer supported.\nRemove (Y/n)? ')
+ if read_bool():
+ out('Removing "' + key + '"\n')
+ data.pop(i)
+ else:
+ break
+
+def pressed(data):
+ i, nul, nul = find_key(data, 'window.button.pressed', True)
+ if i >= 0:
+ out('The window.button.pressed option has been replaced by ' +
+ 'window.button.pressed.focus and ' +
+ 'window.button.pressed.unfocus.\nUpdate (Y/n)? ')
+ if read_bool():
+ l = data[i]
+ out('Removing "window.button.pressed"\n')
+ data.pop(i)
+ out('Adding "window.button.pressed.unfocus"\n')
+ data.insert(i, l.replace('window.button.pressed',
+ 'window.button.pressed.unfocus'))
+ out('Adding "window.button.pressed.focus"\n')
+ data.insert(i, l.replace('window.button.pressed',
+ 'window.button.pressed.focus'))
+
+def x_fonts(data):
+ i, nul, nul = find_key(data, 'window.font')
+ if i >= 0:
+ out('You appear to specify fonts using the old X fonts ' +
+ 'syntax.\nShall I remove all fonts from the theme (Y/n)? ')
+ if not read_bool():
+ return
+ else: return
+ while 1:
+ i, key, nul = find_key(data, '.font')
+ if i < 0:
+ break
+ out('Removing "' + key + '"\n')
+ data.pop(i)
+
+def xft_fonts(data):
+ i, nul, nul = find_key(data, '.xft.')
+ if i >= 0:
+ out('You appear to specify fonts using the old Xft fonts ' +
+ 'syntax.\nShall I update these to the new syntax (Y/n)? ')
+ if not read_bool():
+ return
+ else: return
+ fonts = {}
+ fonts['window'] = 'window.label.focus.font'
+ fonts['menu.items'] = 'menu.items.font'
+ fonts['menu.title'] = 'menu.title.font'
+ for f in fonts.keys():
+ li, nul, flags = find_key(data, f + '.xft.flags')
+ if li < 0:
+ li, nul, flags = find_key(data, '*.xft.flags')
+ else:
+ out('Removing ' + f + '.xft.flags\n')
+ data.pop(li)
+ oi, nul, offset = find_key(data, f + '.xft.shadow.offset')
+ if oi < 0:
+ oi, nul, offset = find_key(data, '*.xft.shadow.offset')
+ else:
+ out('Removing ' + f + '.xft.shadow.offset\n')
+ data.pop(oi)
+ ti, nul, tint = find_key(data, f + '.xft.shadow.tint')
+ if ti < 0:
+ ti, nul, tint = find_key(data, '*.xft.shadow.tint')
+ else:
+ out('Removing ' + f + '.xft.shadow.tint\n')
+ data.pop(ti)
+ fi, nul, face = find_key(data, f + '.xft.font')
+ if fi < 0:
+ fi, nul, face = find_key(data, '*.xft.font')
+ if fi >= 0: fi = len(data) - 1
+ else:
+ out('Removing ' + f + '.xft.font\n')
+ data.pop(fi)
+
+ if fi >= 0:
+ s = face
+ if li >= 0:
+ if flags.find('bold'):
+ s = s + ':bold'
+ if flags.find('shadow'):
+ s = s + ':shadow=y'
+ if oi >= 0:
+ s = s + ':shadowoffset=' + offset
+ if ti >= 0:
+ s = s + ':shadowtint=' + tint
+ out('Adding ' + fonts[f] + '\n')
+ data.insert(fi, fonts[f] + ': ' + s)
+
+ for stars in ('*.xft.flags', '*.xft.shadow.offset' ,
+ '*.xft.shadow.tint', '*.xft.font'):
+ i, key, nul = find_key(data, stars)
+ if i >= 0:
+ out('Removing ' + key + '\n')
+ data.pop(i)
+
+def pixelsize(data):
+ fonts = ('window.label.focus.font',
+ 'menu.items.font',
+ 'menu.title.font')
+ for f in fonts:
+ i, key, value = find_key(data, f, True)
+ if value:
+ if value.find('pixelsize') == -1:
+ out('*** ERROR *** The ' + key + ' font size is not being '
+ 'specified by pixelsize. It is recommended that you use '
+ 'pixelsize instead of pointsize for specifying theme '
+ 'fonts. e.g. "sans:pixelsize=12"\n')
+ global valid
+ valid = False
+
+def warn_missing(data):
+ need = ('window.active.button.hover', 'window.inactive.button.hover',
+ 'menu.overlap')
+ for n in need:
+ i, nul, nul = find_key(data, n)
+ if i < 0:
+ out('The ' + n + ' value was not found in the theme, but it '
+ 'can optionally be set.\n')
+
+def err_missing(data):
+ need = ('window.active.button.disabled',
+ 'window.inactive.button.disabled',
+ 'window.active.client.color',
+ 'window.inactive.client.color')
+ for n in need:
+ i, nul, nul = find_key(data, n)
+ if i < 0:
+ out('*** ERROR *** The ' + n + ' value was not found in the '
+ 'theme, but it is required to be set.\n')
+ global valid
+ valid = False
+
+
+def usage():
+ out('Usage: themupdate.py /path/to/themerc > newthemerc\n\n')
+ sys.exit()
+
+try:
+ file = open(sys.argv[1])
+except IndexError:
+ usage()
+except IOError:
+ out('Unable to open file "' + sys.argv[1] + '"\n\n')
+ usage()
+
+data = file.readlines()
+for i in range(len(data)):
+ data[i] = data[i].strip()
+
+simple_replace(data)
+replace_colors(data)
+remove(data)
+pressed(data)
+x_fonts(data)
+xft_fonts(data)
+pixelsize(data)
+warn_missing(data)
+err_missing(data)
+
+for l in data:
+ print l
+
+sys.exit(not valid)
diff --git a/version.h.in b/version.h.in
new file mode 100644
index 0000000..0602a2a
--- /dev/null
+++ b/version.h.in
@@ -0,0 +1,6 @@
+#ifndef ob__version_h
+#define ob__version_h
+
+#define OPENBOX_VERSION "@OB_VERSION@"
+
+#endif